package com.technophobia.substeps.swtbot.tests;
import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.withMnemonic;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.bindings.keys.ParseException;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEclipseEditor;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotEditor;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
import org.eclipse.swtbot.swt.finder.keyboard.Keyboard;
import org.eclipse.swtbot.swt.finder.keyboard.Keystrokes;
import org.eclipse.swtbot.swt.finder.results.Result;
import org.eclipse.swtbot.swt.finder.results.VoidResult;
import org.eclipse.swtbot.swt.finder.results.WidgetResult;
import org.eclipse.swtbot.swt.finder.utils.Position;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTableItem;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
import org.eclipse.ui.PlatformUI;
import org.hamcrest.Matcher;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(SWTBotJunit4ClassRunner.class)
public class BasicTest {
private static SWTWorkbenchBot bot;
private static SWTBotShell outerShell;
/*
*
* swtbot api
* http://download.eclipse.org/technology/swtbot/helios/dev-build/apidocs/
*
* // fire up an eclipse with the right plugin?
*
* http://wiki.eclipse.org/SWTBot/Automate_test_execution - have a look at
* the maven tycho examples
*/
@BeforeClass
public static void beforeClass() throws Exception {
// http://www.eclipse.org/forums/index.php/t/166612/
SWTBotPreferences.KEYBOARD_LAYOUT = "EN_US"; // doesn't like en_GB
bot = new SWTWorkbenchBot();
outerShell = bot.activeShell();
outerShell.activate();
}
private void setupSubStepsProject() {
switchToJavaPerspective();
File f = new File("vanilla_workspace/test-substeps-project");
importProjectIntoWorkspace("test-substeps-project", f.getAbsolutePath());
System.out.println("done setupSubStepsProject");
}
public Shell getActiveShell() {
return UIThreadRunnable.syncExec(new WidgetResult<Shell>() {
@Override
public Shell run() {
Shell rtn = null;
Shell active = PlatformUI.getWorkbench().getDisplay().getActiveShell();
if (active == null) {
Shell[] shells = PlatformUI.getWorkbench().getDisplay().getShells();
for (Shell s : shells) {
if (s.isFocusControl()) {
rtn = s;
}
}
} else {
rtn = active;
}
return rtn;
}
});
}
private void importProjectIntoWorkspace(String projectName, String path) {
bot.menu("File").menu("Import...").click();
// are we still in the same shell now ?
SWTBotTreeItem[] allItems = bot.tree().getAllItems();
SWTBotTreeItem generalTreeItem = null;
if (allItems != null) {
for (SWTBotTreeItem it : allItems) {
if (it.getText().equalsIgnoreCase("General")) {
generalTreeItem = it;
break;
}
}
}
if (generalTreeItem != null) {
generalTreeItem.expand();
generalTreeItem.getNode(1).select();
bot.button("Next >").click();
bot.text().setText(path);
bot.button("Refresh").click();
bot.tree().getTreeItem(projectName + " (" + path + ")").select();
bot.checkBox("Copy projects into workspace").click();
bot.button("Finish").click();
}
}
private void switchToJavaPerspective() {
bot.menu("Window").menu("Open Perspective").menu("Java").click();
Assert.assertEquals("Java", bot.activePerspective().getLabel());
}
@Test
public void testBasicEditorFunctionality() throws Exception {
setupSubStepsProject();
// TODO how to wait till we're up and running?
bot.sleep(1000 * 10);
// initial setup should have some errors
testForInitialErrors();
setupSubstepsProjectForMavenPattern("test-substeps-project");
cleanAndBuildProject("test-substeps-project");
bot.sleep(1000 * 5);
testForNoErrors();
String[] feature = new String[] { "src/test/resources", "features", "plugin-tests-sample.feature" };
runFeature("test-substeps-project", feature);
bot.sleep(1000 * 5);
checkSubtepsView();
checkOpenSubstepDefinition();
checkSubstepFileSuggestions();
}
private void checkSubstepFileSuggestions() {
SWTBotEditor activeEditor = bot.activeEditor();
SWTBotEclipseEditor textEditor = activeEditor.toTextEditor();
int lineCount = textEditor.getLineCount();
int len = textEditor.getTextOnLine(lineCount-1).length();
System.out.println("substep line count: " + lineCount + " len: " + len);
textEditor.navigateTo(lineCount -1 , len);
System.out.println("current line: " + textEditor.getTextOnCurrentLine());
textEditor.typeText("\n");
List<String> proposals = textEditor.getAutoCompleteProposals("Ca");
Assert.assertThat(proposals.size(), is(4));
Assert.assertThat(proposals.get(0), is("CallMethod One with parameter \"<value>\""));
Assert.assertThat(proposals.get(1), is("CallMethod Three"));
Assert.assertThat(proposals.get(2), is("CallMethod Two"));
Assert.assertThat(proposals.get(3), is("CallMethod four"));
// not sure how to select one of the proposals, do we even need to ?
textEditor.save();
}
private Position getPositionThatIncludesText(SWTBotEclipseEditor textEditor, String text){
return getPositionThatIncludesText(textEditor, text, 1);
}
private Position getPositionThatIncludesText(SWTBotEclipseEditor textEditor, String text, int occurence){
int lineNumber = 0;
int col = -1;
boolean found = false;
for (String line: textEditor.getLines()){
System.out.println("text editor line: " + line);
if (line != null && line.contains(text)){
if (occurence == 1){
// found the line
col = line.indexOf(text) + 1;
found = true;
break;
}
else {
occurence--;
}
}
lineNumber++;
}
Assert.assertTrue("didn't find expected text[ " + text + "] in editor", found);
return new Position(lineNumber, col);
}
private KeyStroke getKeyStroke(String key){
KeyStroke keyStroke = null;
try {
keyStroke = KeyStroke.getInstance(key);
} catch (ParseException e) {
Assert.fail(e.getMessage());
}
Assert.assertNotNull(key);
return keyStroke;
}
private void checkOpenSubstepDefinition() {
bot.tree().getTreeItem("test-substeps-project").
getNode("src/test/resources").getNode("features").getNode("plugin-tests-sample.feature").doubleClick();
SWTBotEditor featureEditor = bot.editorByTitle("plugin-tests-sample.feature");
featureEditor.setFocus();
SWTBotEclipseEditor textEditor = featureEditor.toTextEditor();
System.out.println("text on line 0: " + textEditor.getTextOnLine(0));
String substep = "And I can call a method that doesn't exist yet";
Position position = getPositionThatIncludesText(textEditor, substep);
textEditor.navigateTo(position);
System.out.println("current line text is: " + textEditor.getTextOnCurrentLine());
textEditor.pressShortcut(getKeyStroke("F3"));
// should now have a substeps file open
SWTBotEditor activeEditor = bot.activeEditor();
textEditor = activeEditor.toTextEditor();
Assert.assertThat(activeEditor.getTitle(), is("plugin-tests-sample.substeps"));
Assert.assertTrue(
textEditor.getTextOnCurrentLine().contains("Define: " + substep));
}
private void checkSubtepsView() {
SWTBotView viewByTitle = bot.viewByTitle("Substeps");
viewByTitle.setFocus();
SWTBotTree subStepsTree = viewByTitle.bot().tree();
SWTBotTreeItem[] allItems = subStepsTree.getAllItems();
SWTBotTreeItem substepsTreeItem = null;
for (SWTBotTreeItem item : allItems) {
System.out.println("substeps tree item: " + item.getText());
if (item.getText().contains("Features")) {
item.expand();
substepsTreeItem = item;
}
}
Assert.assertNotNull(substepsTreeItem);
List<String> nodes = substepsTreeItem.getNodes();
System.out.println("nodes size: " + nodes.size());
for (String s : nodes) {
System.out.println("substep node: " + s);
}
FeatureTreeNode rootNode = new FeatureTreeNode("Features");
FeatureTreeNode featureNode = rootNode.addChildNode("A feature to write some scenarios for the plugin tests");
FeatureTreeNode scenarioNode = featureNode.addChildNode("a test scenario");
scenarioNode.addChildNode("Given I can call method one with <param>").addChildNode(
"CallMethod One with parameter \"param\"");
scenarioNode.addChildNode("And I can call a method that doesn't exist yet").addChildNode("CallMethod four");
scenarioNode.addChildNode("Then I can do something else").addChildNode("CallMethod Two");
recurseAndcheckSWTBotTree(substepsTreeItem, rootNode);
}
/**
* a representation of a feature / scenario / step / substep tree
*
* @author ian
*
*/
private static class FeatureTreeNode {
private final String text;
private List<FeatureTreeNode> children;
public FeatureTreeNode(String text) {
this.text = text;
}
public FeatureTreeNode addChildNode(String text) {
FeatureTreeNode child = new FeatureTreeNode(text);
if (this.children == null) {
this.children = new ArrayList<FeatureTreeNode>();
}
this.children.add(child);
return child;
}
public boolean hasChildren() {
return this.children != null && !this.children.isEmpty();
}
public FeatureTreeNode getChild(int i) {
FeatureTreeNode child = null;
if (children != null && i < children.size()) {
child = children.get(i);
}
return child;
}
}
private void recurseAndcheckSWTBotTree(SWTBotTreeItem swtNode, FeatureTreeNode treeNode) {
if (swtNode != null) {
System.out.println("node text: " + swtNode.getText() + " expecting: " + treeNode.text);
Assert.assertTrue(swtNode.getText().startsWith(treeNode.text));
if (swtNode.getNodes() != null && !swtNode.getNodes().isEmpty()) {
for (int i = 0; i < swtNode.getNodes().size(); i++) {
SWTBotTreeItem child = swtNode.getNode(i);
child.expand();
recurseAndcheckSWTBotTree(child, treeNode.getChild(i));
}
}
} else if (treeNode != null) {
Assert.fail("expecting an swt not tree node item with text: " + treeNode.text);
}
}
private void runFeature(String projectName, String[] path) {
SWTBotTreeItem projectTreeItem = bot.tree().getTreeItem(projectName);
SWTBotTreeItem currentParent = projectTreeItem;
for (String pathElement : path) {
System.out.println("finding path element : " + pathElement);
// expand the current parent before looking beneath
currentParent.expand();
currentParent = currentParent.getNode(pathElement);
Assert.assertNotNull("failed to find path element: " + pathElement, currentParent);
}
currentParent.select();
String actualRunMenuText = UIThreadRunnable.syncExec(new Result<String>() {
@Override
public String run() {
MenuItem[] items = bot.menu("Run As").widget.getMenu().getItems();
Assert.assertNotNull(items);
String actualRunMenuText = null;
for (MenuItem menuItem : items) {
if (menuItem.getText().contains("Substeps Feature Test")) {
actualRunMenuText = menuItem.getText();
break;
}
}
return actualRunMenuText;
}
});
Assert.assertNotNull("failed to find the actual run as menu text", actualRunMenuText);
bot.menu("Run As").menu(actualRunMenuText).click();
}
// copied from http://www.eclipse.org/forums/index.php/m/723219/ and
// http://www.eclipse.org/forums/index.php/t/11863/
/**
* Clicks the context menu matching the text.
*
* @param text
* the text on the context menu.
* @throws WidgetNotFoundException
* if the widget is not found.
*/
public static void clickContextMenu(final SWTBotShell shell, final String... texts) {
// fetch
final MenuItem menuItem = getContextMenu(shell, texts);
// click
click(menuItem);
// hide
UIThreadRunnable.syncExec(new VoidResult() {
public void run() {
hide(menuItem.getParent());
}
});
}
private static void click(final MenuItem menuItem) {
final Event event = new Event();
event.time = (int) System.currentTimeMillis();
event.widget = menuItem;
event.display = menuItem.getDisplay();
event.type = SWT.Selection;
UIThreadRunnable.asyncExec(menuItem.getDisplay(), new VoidResult() {
public void run() {
menuItem.notifyListeners(SWT.Selection, event);
}
});
}
private static void hide(final Menu menu) {
menu.notifyListeners(SWT.Hide, new Event());
if (menu.getParentMenu() != null) {
hide(menu.getParentMenu());
}
}
private static MenuItem show(final Menu menu, final Matcher<?> matcher) {
if (menu != null) {
menu.notifyListeners(SWT.Show, new Event());
MenuItem[] items = menu.getItems();
for (final MenuItem menuItem : items) {
if (matcher.matches(menuItem)) {
return menuItem;
}
}
menu.notifyListeners(SWT.Hide, new Event());
}
return null;
}
private static MenuItem getContextMenu(final SWTBotShell shell, final String... texts) {
final MenuItem menuItem = getContextMenuImpl(shell, texts);
if (menuItem == null) {
throw new WidgetNotFoundException("Could not find menu: " + texts);
// + computeMenuTrace(texts));
}
return menuItem;
}
private static MenuItem getContextMenuImpl(final SWTBotShell shell, final String... texts) {
// traceMenu(texts);
// show
final MenuItem menuItem = UIThreadRunnable.syncExec(new WidgetResult<MenuItem>() {
public MenuItem run() {
MenuItem menuItem = null;
Menu menu = shell.widget.getMenuBar();
for (String text : texts) {
@SuppressWarnings("unchecked")
Matcher<?> matcher = allOf(instanceOf(MenuItem.class), withMnemonic(text));
menuItem = show(menu, matcher);
if (menuItem != null) {
menu = menuItem.getMenu();
} else {
hide(menu);
break;
}
}
return menuItem;
}
});
return menuItem;
}
private void cleanAndBuildProject(String projectName) {
System.out.println("cleanAndBuildProject: " + projectName);
SWTBotMenu menu = new SWTBotMenu(getContextMenu(bot.activeShell(), "Project", "Clean..."));
menu.click();
bot.shell("Clean").activate();
bot.button().click();
System.out.println("cleanAndBuildProject: " + projectName + " done");
}
private void testForNoErrors() {
SWTBotView viewByTitle = bot.viewByTitle("Problems");
viewByTitle.setFocus();
SWTBotTree problemsTree = viewByTitle.bot().tree();
SWTBotTreeItem[] allItems = problemsTree.getAllItems();
for (SWTBotTreeItem item : allItems) {
Assert.assertFalse(item.getText().contains("Errors"));
}
}
private void testForInitialErrors() {
SWTBotView viewByTitle = bot.viewByTitle("Problems");
viewByTitle.setFocus();
SWTBotTree problemsTree = viewByTitle.bot().tree();
SWTBotTreeItem[] allItems = problemsTree.getAllItems();
SWTBotTreeItem errorsTreeItem = null;
for (SWTBotTreeItem item : allItems) {
System.out.println("probs view tree item: " + item.getText());
if (item.getText().contains("Errors")) {
item.expand();
errorsTreeItem = item;
}
}
Assert.assertNotNull(errorsTreeItem);
List<String> nodes = errorsTreeItem.getNodes();
System.out.println("nodes size: " + nodes.size());
Assert.assertTrue(nodes.size() == 3);
for (String s : nodes) {
System.out.println("error node: " + s);
}
Assert.assertTrue(errorsTreeItem
.getNode(0)
.getText()
.startsWith(
"Duplicate pattern detected: Pattern [And I can call a method that doesn't exist yet] is defined"));
Assert.assertTrue(errorsTreeItem
.getNode(1)
.getText()
.startsWith(
"Duplicate pattern detected: Pattern [Given I can call method one with \"?([^\"]*)\"?] is defined in"));
Assert.assertTrue(errorsTreeItem.getNode(2).getText()
.startsWith("Duplicate pattern detected: Pattern [Then I can do something else]"));
}
private void setupSubstepsProjectForMavenPattern(String projectName) {
bot.tree().getTreeItem(projectName).select();
bot.menu("Properties").click();
bot.text().setText("Substeps");
bot.tree().getTreeItem("Substeps").select();
bot.checkBox("Enable project specific settings").click();
bot.textWithLabel("Feature folder").setText("src/test/resources/features");
bot.textWithLabel("Substeps folder").setText("src/test/resources/substeps");
bot.button("Apply").click();
bot.button("OK").click();
}
@AfterClass
public static void sleep() {
// bot.sleep(2000);
System.out.println("closing all shells");
bot.closeAllShells();
}
}