org.testeditor.fixture.swt.SWTFixture.java Source code

Java tutorial

Introduction

Here is the source code for org.testeditor.fixture.swt.SWTFixture.java

Source

/*******************************************************************************
 * Copyright (c) 2012 - 2016 Signal Iduna Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Signal Iduna Corporation - initial API and implementation
 * akquinet AG
 * itemis AG
 *******************************************************************************/
package org.testeditor.fixture.swt;

import static org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory.widgetOfType;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.waits.Conditions;
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.ContextMenuHelper;
import org.eclipse.swtbot.swt.finder.utils.SWTBotPreferences;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotButton;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotCheckBox;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotCombo;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotList;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotMenu;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotText;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
import org.eclipse.swtbot.swt.finder.widgets.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testeditor.fixture.core.TestRunListener;
import org.testeditor.fixture.core.TestRunReportable;
import org.testeditor.fixture.core.TestRunReporter;
import org.testeditor.fixture.core.TestRunReporter.Action;
import org.testeditor.fixture.core.TestRunReporter.SemanticUnit;
import org.testeditor.fixture.core.interaction.FixtureMethod;

/**
 * Fixture to control SWT elements in an RCP Application.
 *
 */
public class SWTFixture implements TestRunListener, TestRunReportable {

    private static final Logger logger = LoggerFactory.getLogger(SWTFixture.class);
    private static final int SCREENSHOT_FILENAME_MAXLEN = 128;

    private SWTWorkbenchBot bot = new SWTWorkbenchBot();
    private String runningTest = null;

    public SWTFixture() {
        System.setProperty("org.eclipse.swtbot.search.timeout", "10000");
        System.setProperty("org.eclipse.swtbot.playback.poll.delay", "10000");
    }

    public void initWithReporter(TestRunReporter reporter) {
        reporter.addListener(this);
    }

    @Override
    public void reported(SemanticUnit unit, Action action, String msg) {
        if (unit == SemanticUnit.TEST && action == Action.ENTER) {
            runningTest = msg;
        }
        if (screenshotShouldBeMade(unit, action, msg)) {
            screenshot(msg + '.' + action.name());
        }
    }

    /**
     * Method for debugging the AUT. A first implementation of an widget
     * scanner. all widget s on current SWT-App will be printed with their id's
     * in the swtbot.log with the trace-level. This Method is in work and can be
     * extended and modified step by step.
     * 
     */
    @FixtureMethod
    public void reportWidgets() {
        logger.info("analyzeWidgets start");
        logger.info("---------------------------------------------");

        getDisplay().syncExec(new Runnable() {
            @Override
            public void run() {

                List<? extends Widget> widgets = bot.widgets(widgetOfType(Widget.class));

                StringBuilder sb = new StringBuilder();

                for (Widget widget : widgets) {

                    if (widget instanceof Table) {
                        sb.append("\n >>> Table gefunden mit " + ((Table) widget).getItems().length + " Zeilen !");
                    }

                    sb.append("widgetId: " + widget.getData("org.eclipse.swtbot.widget.key"));
                    sb.append(" widgetClass: " + widget.getClass().getSimpleName());
                    try {
                        Method[] methods = widget.getClass().getMethods();
                        boolean foundGetText = false;
                        for (Method method : methods) {
                            if (method.getName().equals("getText")) {
                                foundGetText = true;
                            }
                        }
                        if (foundGetText) {

                            Method method = widget.getClass().getMethod("getText", new Class[] {});
                            sb.append("\n >>> text value: " + method.invoke(widget, new Object[] {}));
                        }
                    } catch (Exception e) {
                        logger.error("No text", e);
                    }
                    sb.append(" widget: " + widget).append("\n");
                }

                logger.info(sb.toString());

            }
        });
        logger.info("analyzeWidgets end");
        logger.info("---------------------------------------------");
    }

    /**
     * 
     * @return a SWT Display object.
     */
    protected Display getDisplay() {
        return Display.getDefault();
    }

    /**
     * Wait Step to inteerupt the test execution for a defined time.
     * 
     * @param seconds
     *            to wait until continue test.
     * @throws InterruptedException
     *             on test abort.
     */
    @FixtureMethod
    public void wait(int seconds) throws InterruptedException {
        logger.info("Waiting for {} seconds.", seconds);
        Thread.sleep(seconds * 1000);
    }

    /**
     * Waits for a dialog with a given title to show up.
     * 
     * @param title
     *            the title of the dialog
     */
    @FixtureMethod
    public void waitForDialogClosing(String title) {
        long swtBotDefaultInMilliSeconds = SWTBotPreferences.TIMEOUT;
        waitForPopupDialogClosingWithTimeout(title, swtBotDefaultInMilliSeconds / 2000,
                swtBotDefaultInMilliSeconds / 1000);
    }

    private String getCurrentTestCase() {
        return runningTest != null ? runningTest : "UNKNOWN_TEST";
    }

    private String getScreenshotPath() {
        // configurable through maven build?
        return "screenshots";
    }

    private boolean screenshotShouldBeMade(SemanticUnit unit, Action action, String msg) {
        // configurable through maven build?
        return (action == Action.ENTER) || unit == SemanticUnit.TEST;
    }

    private String reduceToMaxLen(String base, int maxLen) {
        if (base.length() < maxLen) {
            return base;
        } else {
            return base.substring(0, maxLen);
        }
    }

    private String constructScreenshotFilename(String filenameBase, String testcase) {
        String additionalGraphicType = ".png";
        String escapedBaseName = filenameBase.replaceAll("[^a-zA-Z0-9.-]", "_").replaceAll("_+", "_")
                .replaceAll("_+\\.", ".").replaceAll("\\._+", ".");
        String timeStr = new SimpleDateFormat("HHmmss.SSS").format(new Date());
        StringBuffer finalFilenameBuffer = new StringBuffer();
        int lenOfFixedElements = timeStr.length() + additionalGraphicType.length() + 1/* hyphen */;
        finalFilenameBuffer //
                .append(getScreenshotPath()) //
                .append('/').append(reduceToMaxLen(testcase, SCREENSHOT_FILENAME_MAXLEN))//
                .append('/').append(timeStr).append('-')
                .append(reduceToMaxLen(escapedBaseName, SCREENSHOT_FILENAME_MAXLEN - lenOfFixedElements))//
                .append(additionalGraphicType);
        return finalFilenameBuffer.toString();
    }

    /**
     * Write a screenshot of the current ui into a file, based on the basic
     * filenameBase provided. The final filename is constructed using the
     * testcase a hash of the fixture itself and a shortened timestamp.
     * 
     * @param filenameBase
     *            user definable part of the final filename
     */
    @FixtureMethod
    public void screenshot(String filenameBase) {
        String testcase = getCurrentTestCase();
        String finalFilename = constructScreenshotFilename(filenameBase, testcase);
        new SWTWorkbenchBot().captureScreenshot(finalFilename);
        logger.info("Wrote screenshot to file='{}'.", finalFilename);
    }

    /**
     * Waits for a dialog with a given title to show up.
     * 
     * @param title
     *            the title of the dialog
     * @param openingTimeout
     *            the time to wait for the window to open
     * @param closingTimeout
     *            the time to wait for the window to close, once it opened up
     */
    @FixtureMethod
    public void waitForPopupDialogClosingWithTimeout(String title, long openingTimeout, long closingTimeout) {
        logger.info("Waiting for dialog with title='{}' to open, timeout='{}' seconds.", title, closingTimeout);
        try {
            try {
                bot.waitUntil(Conditions.shellIsActive(title), openingTimeout * 1000);
                SWTBotShell shell = bot.shell(title);
                bot.waitUntil(Conditions.shellCloses(shell), closingTimeout * 1000);
            } catch (TimeoutException e) {
                logger.info("Dialog with title {} was not found. Test continuous.", title);
            }
        } catch (WidgetNotFoundException e) {
            logger.info("Widget not found. No reason to wait.");
        }
        logger.info("Finished waiting.");
    }

    /**
     * Checks that a view in the RCP is active.
     * 
     * @param locator
     *            to locator of the view to be looked up.
     * @param locatorStrategy
     *            the strategy that shall be applied
     * @return true if the vie is visible
     */
    @FixtureMethod
    public boolean isViewVisible(String locator, ViewLocatorStrategy locatorStrategy) {
        SWTBotView view = getView(locator, locatorStrategy);
        return view.isActive();
    }

    private SWTBotView getView(String locator, ViewLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            logger.debug("Searching for view with id='{}'.", locator);
            return bot.viewById(locator);
        case PARTNAME:
            logger.debug("Searching for view with partName='{}'.", locator);
            return bot.viewByPartName(locator);
        case TITLE:
            logger.debug("Searching for view with title='{}'.", locator);
            return bot.viewByTitle(locator);
        default:
            throw new IllegalArgumentException("Unknown locator strategy: " + locatorStrategy);
        }
    }

    /**
     * Selects an element in a tree. It takes a path string end tries to expand
     * the tree along the path. The node of the path is selectd.
     * 
     * @param locator
     *            of the tree.
     * @param itemName
     *            path to the item to be selectd. Example:
     *            "Root/SubNode/FinalNode"
     */
    @FixtureMethod
    public void selectElementInTreeView(String itemName, String locator, SWTLocatorStrategy locatorStrategy) {
        logger.trace("search for view with title: {} to get the default tree", locator);
        SWTBotTree tree = getTree(locator, locatorStrategy);
        logger.trace("Open item with path: {}", itemName);
        try {
            SWTBotTreeItem expandNode = tree.expandNode(itemName.split("/"));
            expandNode.select();
        } catch (WidgetNotFoundException e) {
            logger.error("Widget not found", e);
            printTreeItems(tree, locator);
            throw e;
        }
    }

    private SWTBotTree getTree(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.viewById(locator).bot().tree();
        case LABEL:
            return bot.viewByTitle(locator).bot().tree();
        case SINGLE:
            return bot.tree();
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    private void printTreeItems(SWTBotTree tree, String locator) {
        logger.info("Printing all items of tree with locator='{}'.", locator);
        printTreeItems(tree.getAllItems(), 0);
    }

    private void printTreeItems(SWTBotTreeItem[] items, int level) {
        String spaces = StringUtils.repeat(" ", 4 * level);
        for (SWTBotTreeItem item : items) {
            logger.info("{}|-- {}", spaces, item.getText());
            if (!item.isExpanded()) {
                item.expand();
            }
            printTreeItems(item.getItems(), level + 1);
        }
    }

    @FixtureMethod
    public void selectElementInList(String itemName, String locator, SWTLocatorStrategy locatorStrategy) {
        logger.trace("search for list with {}", locator);
        SWTBotList list = getList(locator, locatorStrategy);
        list.select(itemName);
    }

    private SWTBotList getList(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.listWithId(locator);
        case LABEL:
            return bot.listWithLabel(locator);
        case SINGLE:
            return bot.list();
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    @FixtureMethod
    public void selectElementInCombobox(String value, String locator, SWTLocatorStrategy locatorStrategy) {
        logger.trace("search for dropdown with {}", locator);
        SWTBotCombo comboBox = getComboBox(locator, locatorStrategy);
        comboBox.setText(value);
    }

    private SWTBotCombo getComboBox(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.comboBoxWithId(locator);
        case LABEL:
            return bot.comboBoxWithLabel(locator);
        case SINGLE:
            return bot.comboBox();
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    /**
     * Opens the context Menu of a widget and executes the menuitem described as
     * a path.
     * 
     * @param viewName
     *            id of the view / widget with the context menu
     * @param menuItem
     *            path to the menu item Example: "New/Project"
     * @throws Exception
     *             on failure to break the test.
     */
    @FixtureMethod
    public void executeContextMenuEntry(String menuItem, String viewName, SWTLocatorStrategy locatorStrategy)
            throws Exception {
        logger.trace("Search for view with title: {}", viewName);
        SWTBotTree tree = getTree(viewName, locatorStrategy);
        assertNotNull(tree);
        MenuItem item = null;
        try {
            item = ContextMenuHelper.contextMenu(tree, menuItem.split("/"));
        } catch (WidgetNotFoundException e) {
            Thread.sleep(100);
            logger.trace("Search menu entry a second time: {}", menuItem);
            item = ContextMenuHelper.contextMenu(tree, menuItem.split("/"));
        }
        assertNotNull(item);
        logger.trace("Click on menu item: {}", menuItem);
        new SWTBotMenu(item).click();
    }

    /**
     * Looking for a widget in that is type able and is identifiable by the
     * locator.
     * 
     * @param locator
     *            of the widget.
     * @param value
     *            set to the widget.
     * @param locatorStrategy
     *            strategy to lookup the widget.
     */
    @FixtureMethod
    public void typeInto(String value, String locator, SWTLocatorStrategy locatorStrategy) {
        logger.trace("search for text with title: {}", locator);
        SWTBotText text = getText(locator, locatorStrategy);
        text.setText(value);
    }

    private SWTBotText getText(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.textWithId(locator);
        case LABEL:
            return bot.textWithLabel(locator);
        case SINGLE:
            return bot.text();
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    /**
     * Clicks on a button.
     * 
     * @param locator
     *            to identify the button.
     */
    @FixtureMethod
    public void clickOn(String locator, SWTLocatorStrategy locatorStrategy) {
        logger.trace("search for button with: {}", locator);
        try {
            SWTBotButton button = getButton(locator, locatorStrategy);
            button.click();
        } catch (WidgetNotFoundException e) {
            logger.info(e.getLocalizedMessage(), e);
            this.reportWidgets();
            fail();
        }
    }

    private SWTBotButton getButton(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.buttonWithId(locator);
        case LABEL:
            return bot.button(locator);
        case SINGLE:
            return bot.button();
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    /**
     * Checks an SWT checkbox.
     * 
     * @param locator
     *            to identify the checkbox
     * @param locatorStrategy
     *            the strategy to use
     */
    @FixtureMethod
    public void check(String locator, SWTLocatorStrategy locatorStrategy) {
        SWTBotCheckBox checkBox = getCheckBox(locator, locatorStrategy);
        checkBox.select();
    }

    /**
     * Unchecks an SWT checkbox.
     * 
     * @param locator
     *            to identify the checkbox
     * @param locatorStrategy
     *            the strategy to use
     */
    @FixtureMethod
    public void uncheck(String locator, SWTLocatorStrategy locatorStrategy) {
        SWTBotCheckBox checkBox = getCheckBox(locator, locatorStrategy);
        checkBox.deselect();
    }

    // TODO would be nicer to use the generic withId(...) but we need Harmcrest
    // on the classpath for that
    public SWTBotCheckBox getCheckBox(String locator, SWTLocatorStrategy locatorStrategy) {
        switch (locatorStrategy) {
        case ID:
            return bot.checkBoxWithId(locator);
        case LABEL:
            return bot.checkBoxWithLabel(locator);
        case SINGLE:
            return bot.checkBox(locator);
        }
        throw new IllegalArgumentException("Unkown locatorStrategy: " + locatorStrategy);
    }

    private String condenseForLogging(String largeString) {
        String trimmedLargeString = largeString.trim();
        int length = trimmedLargeString.length();
        if (length < 50) {
            return trimmedLargeString;
        }
        return trimmedLargeString.substring(0, 20) + "..." + trimmedLargeString.substring(length - 21, length - 1);
    }

    @FixtureMethod
    public boolean containsActiveTextEditorContent(String searchString) {
        SWTBotEditor activeEditor = bot.activeEditor();
        logger.info("Check if the current active editor '{}' contains '{}'", activeEditor.getTitle(), searchString);
        String textEditorContents = activeEditor.toTextEditor().getText();
        boolean result = textEditorContents.contains(searchString);
        logger.debug("Editor content: '{}' contains '{}' is: {}.", textEditorContents, searchString, result);
        return result;
    }

    @FixtureMethod
    public void removeLineFromEditor(int lineNumber) {
        SWTBotEditor activeEditor = bot.activeEditor();
        logger.info("Removing line '{}' from  editor '{}'.", lineNumber, activeEditor.getTitle());
        SWTBotEclipseEditor textEditor = activeEditor.toTextEditor();
        String contents = textEditor.getText();
        logger.debug("Editor contains '{}' before.", condenseForLogging(contents));
        String newContent = removeLineFromString(contents, lineNumber);
        textEditor.setText(newContent);
        String condensedContentsAfter = condenseForLogging(textEditor.getText());
        logger.debug("Editor contains '{}' after.", condensedContentsAfter);
    }

    private String removeLineFromString(String string, int lineNumber) {
        String[] contentLines = string.replaceAll("\r", "").split("\n");
        logger.debug("Content contains '{}' lines, now removing line '{}'.", contentLines.length, lineNumber);
        if (lineNumber < 1 || lineNumber > contentLines.length) {
            throw new IllegalArgumentException(
                    "Linenumber='" + lineNumber + "' outside available lines='" + contentLines.length + "'.");
        }
        StringBuffer newContent = new StringBuffer();
        for (int i = 0; i < contentLines.length; i++) {
            if (i != lineNumber - 1) {
                if (newContent.length() > 0) {
                    newContent.append(System.lineSeparator());
                }
                newContent.append(contentLines[i]);
            }
        }
        return newContent.toString();
    }

    @FixtureMethod
    public void saveActiveEditor() {
        SWTBotEditor activeEditor = bot.activeEditor();
        String textEditorContents = activeEditor.toTextEditor().getText();
        String condensedContents = condenseForLogging(textEditorContents);
        logger.debug("Editor contains '{}'.", condensedContents);
        logger.info("Save editor {}", activeEditor.getTitle());
        activeEditor.toTextEditor().save();
    }

    @FixtureMethod
    public void typeTextIntoActiveEditor(String text) {
        SWTBotEditor activeEditor = bot.activeEditor();
        logger.info("Type text: {} into editor {}", text, activeEditor.getTitle());
        activeEditor.toTextEditor().typeText(text);
    }

    @FixtureMethod
    public void goToLineInActiveEditor(int lineNumber) {
        SWTBotEditor activeEditor = bot.activeEditor();
        logger.info("Go to line: {} into editor {}", lineNumber, activeEditor.getTitle());
        activeEditor.toTextEditor().navigateTo(lineNumber, 0);
    }

    @FixtureMethod
    public void activateAutocompletion(String text, String selectedProposal) {
        SWTBotEditor activeEditor = bot.activeEditor();
        logger.info("Activate autocompletion on editor {}", activeEditor.getTitle());
        activeEditor.toTextEditor().autoCompleteProposal(text, selectedProposal);
    }

}