/*
* Copyright 2008-2011 Nokia Siemens Networks Oyj
*
* 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 org.robotframework.swing;
import org.netbeans.jemmy.JemmyProperties;
import org.netbeans.jemmy.TestOut;
import org.robotframework.javalib.library.AnnotationLibrary;
import org.robotframework.javalib.library.KeywordDocumentationRepository;
import org.robotframework.javalib.library.RobotJavaLibrary;
import org.robotframework.swing.keyword.timeout.TimeoutKeywords;
import org.robotframework.swing.util.StandardOutOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class SwingLibrary implements KeywordDocumentationRepository, RobotJavaLibrary {
public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL";
public static SwingLibrary instance;
private final AnnotationLibrary annotationLibrary = new AnnotationLibrary(
"org/robotframework/swing/keyword/**/*.class");
private static final String LIBRARY_DOCUMENTATION = "SwingLibrary is a Robot Framework test library for testing Java Swing user interfaces.\n\n"
+ "It uses a tool called [http://java.net/projects/jemmy/|Jemmy] internally to operate on Swing components.\n"
+ "= Getting Started =\n"
+ "First, the SwingLibrary needs to be taken into use in the settings table:\n"
+ "| *Settings * | *Value* |\n"
+ "| Library | SwingLibrary |\n"
+ "The tested application can be started with keyword `Start Application`, using the name "
+ "of the main application class as an argument:\n"
+ "| `Start Application` | com.acme.TheApplication |\n"
+ "| `Select Window` | TheApplication Window |\n"
+ "| `Push Button` | AcmeButton |\n"
+ "When the tests are executed, both the SwingLibrary and the application and all its dependencies "
+ "need to be available in the CLASSPATH. Robot Framework needs to be started with `jybot` start script "
+ "when using the SwingLibrary. In Windows, this can be done like:\n"
+ "| set CLASSPATH=swinglibrary-<version>.jar;myApp.jar\n"
+ "| jybot my_test.txt\n"
+ "and in *nix like this:\n"
+ "| CLASSPATH=swinglibrary-<version>.jar:myApp.jar jybot my_test.txt\n"
+ "= Contexts =\n"
+ "Keywords that operate on a component always search for the component in some context, "
+ "which has to explicitly set. "
+ "Allowed contexts are windows, dialogs, internal frames, and tabbed panes. "
+ "After a context has been selected, all subsequent keywords search for components in that context "
+ "until a new context is selected. Keywords that can be used to select a context are "
+ "`Select Window`, `Select Dialog` and `Select Context`. For example:\n"
+ "| `Select Window` | My App |\n"
+ "| `Select From Main Menu` | File|Exit |\n"
+ "| `Select Dialog` | Confirm |\n"
+ "| `Push Button` | No |\n"
+ "| `Select Window` | My App |\n"
+ "= Locating components =\n"
+ "Most of the keywords that operate on a visible component take an argument named `identifier`, "
+ "which is used to locate the element. The first matching element is operated on, according to these rules:\n"
+ "- If the `identifier` is a number, it is used as a zero-based index for the particular component type in "
+ "the current context. Using indices is, however, fragile and is strongly discouraged.\n"
+ "- If the `identifier` matches to internal name of a component (set using `setName` method in Java code), that component is chosen.\n"
+ "- For components that have visible text (e.g. buttons), `identifier` is also matched against that.\n"
+ "- Text field keywords also support accessing awt-text fields by prefixing the identifier with awt=.\n"
+ "Keyword `List Components in Context` lists all components and their names and indices in a given context.\n"
+ "= Running keywords in separate threads =\n"
+ "Some actions may cause dialogs or other components to pop up and the keyword would then not return until\n"
+ "the new dialog is closed. In these situations the keyword should be executed in a separate thread,\n"
+ "`Run Keyword In Separate Thread` that test execution can continue.\n"
+ "= Logging =\n"
+ "Starting from version 1.8.0, the internal logging of Jemmy is available on Robot logs, when using DEBUG log level.";
public SwingLibrary() {
this(Collections.<String> emptyList());
}
protected SwingLibrary(final String keywordPattern) {
this(new ArrayList<String>() {
{
add(keywordPattern);
}
});
}
protected SwingLibrary(Collection<String> keywordPatterns) {
addKeywordPatterns(keywordPatterns);
disableOutput();
setDefaultTimeouts();
instance = this;
}
private void addKeywordPatterns(Collection<String> keywordPatterns) {
for (String pattern : keywordPatterns) {
annotationLibrary.addKeywordPattern(pattern);
}
}
@Override
public Object runKeyword(String keywordName, Object[] args) {
return annotationLibrary.runKeyword(keywordName, toStrings(args));
}
@Override
public String[] getKeywordArguments(String keywordName) {
return annotationLibrary.getKeywordArguments(keywordName);
}
@Override
public String getKeywordDocumentation(String keywordName) {
if (keywordName.equals("__intro__"))
return LIBRARY_DOCUMENTATION;
return annotationLibrary.getKeywordDocumentation(keywordName);
}
@Override
public String[] getKeywordNames() {
return annotationLibrary.getKeywordNames();
}
private void setDefaultTimeouts() {
new TimeoutKeywords().setJemmyTimeouts("10");
}
private void disableOutput() {
TestOut out = new StandardOutOutput();
JemmyProperties.setCurrentOutput(out);
}
private Object[] toStrings(Object[] args) {
Object[] newArgs = new Object[args.length];
for (int i = 0; i < newArgs.length; i++) {
if (args[i].getClass().isArray()) {
newArgs[i] = args[i];
} else {
newArgs[i] = args[i].toString();
}
}
return newArgs;
}
}