Discussion:
[swtbot-dev] Workaround for Native Dialogs
Jan Petranek
2009-09-23 19:44:11 UTC
Permalink
Hi there,

like probably a couple other testers before me, I got stuck when I
tried to test native dialogs (file selection dialog...).

Until Eclipse makes those dialogs accessible (obviously the best
solution), we can do with a work-around. I tried replacing the native
dialogs with (testable) SWT-Pendants during the test cycle. I've
already done this for a file dialog and for a simple Message box.

Replacing the MessageBox is pretty much straightforward, I simply
replace it with a MessageDialog.

The file selection dialog is more complex. I simply replaced it with
an Input-Dialog, where SWTBot can enter the path to the file.

Reason for this simplicity.

The code, that displays the file selection dialog is only interested
in two cases:
- Dialog returns some String -> Now, I check, if it represents a
usable file (exists, is readable, writeable ...)
- null or and empty String is returned -> the user canceled the operation

When I'm testing, I don't want to click through my folder structure, I
just want to enter some fixed path (as this is known in the test).

To switch between production mode with native dialogs and test mode
with those fake dialogs, I use a static factory that handles those
dialogs. At the beginning of the test cycle, the factory is set to
test-mode.

If you are interested in this approach, I will happily contribute this
workaround.

All in all, this solution is far from perfect, but it helps me to test
my user interface. As my application is file-centric, it boils down to
this workaround or no meaningful GUI-testing.

Greetings,

Jan Petranek
Ketan Padegaonkar
2009-09-24 01:53:33 UTC
Permalink
Post by Jan Petranek
Until Eclipse makes those dialogs accessible (obviously the best
solution), we can do with a work-around. I tried replacing the native
dialogs with (testable) SWT-Pendants during the test cycle. I've
already done this for a file dialog and for a simple Message box.
I suppose all the magic you mention happens using what you call
swt-pendants -- which seem like some form of a drop-in replacement for
swt widgets.

Would you like to talk about what it is and how they work ? Even better
show some sample code :)
--
Ketan
http://studios.thoughtworks.com/twist | http://twitter.com/ketanpkr
Jan Petranek
2009-09-24 21:55:14 UTC
Permalink
I suppose all the magic you mention happens using what you call swt-pendants
-- which seem like some form of a drop-in replacement for swt widgets.
Exactly, once you see the code, that magic will be pretty gone ;-)
Would you like to talk about what it is and how they work ? Even better show
some sample code :)
Sure. I'll clean up the code a little bit and provide some example
SWTBot-Test code. But since I just got my PDE-build running again,
I'll do it in the next couple of days.

Have a nice weekend,

Jan Petranek

PS: Thank you and all contributors for your work on SWTBot.
Jan Petranek
2009-09-25 20:32:12 UTC
Permalink
Hello co-testers, hello Ketan Padegaonkar,

it's time to keep my promise and show you the code for the SWT
replacements. I simply pasted them into the mail. You can use it under
the same license terms, as SWTBot itself. If want the code in a more
suitable form (zipped eclipse project...), drop me a mail.

The first code is the core of this replacement - it is a static
factory, that your application code can use to show dialogs.

===================== Code Listing NativeDialogFactory
======================================


/*******************************************************************************
* Copyright (c) 2009 Jan Petranek.
* 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
*
*******************************************************************************/

import org.apache.log4j.Logger;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;

/**
* The Class NativeDialogFactory is a configurable Dialog factory.
*
* By default, it handles user dialogs with native dialogs (SWT).
* When the state is set to TESTING, it displays stand-in dialogs.
* Those stand-ins may be way simpler than the native widgets,
* but they are accessible by SWTBot.
*
* @author Jan Petranek
*/
public class NativeDialogFactory {

/** The Log4j logger instance. */
static final Logger logger = Logger.getLogger(NativeDialogFactory.class);

/**
* The Enumeration DialogState, used to indicate the mode of operation.
*/
public enum OperationMode {

/** The DEFAULT state, for normal operation. */
DEFAULT,
/** The TESTING state, used to indicate SWTBot-Testing needs
stand-in dialogs. */
TESTING
};

/** The mode of operation. */
private static OperationMode mode = OperationMode.DEFAULT;

/**
* Sets the operation mode.
*
* @param state the desired operation mode
*/
public static void setMode(OperationMode mode) {
NativeDialogFactory.mode = mode;
}

/**
* Gets the operation mode.
*
* @return the current operation mode
*/
public static OperationMode getMode() {
return mode;
}

/**
* Shows a file selection dialog.
*
* In default mode, this displays a native file selection dialog.
* In testing mode, a simple InputDialog is displayed, where the path
can be entered as a String.
*
* @param shell the parent shell
* @param text title for the file selection dialog
* @param style the style of the file dialog, applies only to native
dialogs (SWT.SAVE| SWT.OPEN | SWT.MULTI)
*
* @return a String with the selected file name. It is still up to
the user to check, if the
* filename is valid. When the user has aborted the file selection,
this returns null.
*
*/
public static String fileSelectionDialog(Shell shell, String text, int style) {
switch (getMode()) {

case DEFAULT: {
// File standard dialog
FileDialog fileDialog = new FileDialog(shell, style);
fileDialog.setText(text);
return fileDialog.open();
}
case TESTING: {
InputDialog fileDialog = new InputDialog(shell, text,
"Select a file", "", new DummyInputValidator());
fileDialog.open();
return fileDialog.getValue();
}

default:
final String msg = "Reached default case in
NativeDialogFactory.fileSelectionDialog, this is a bug, unknown state
"
+ getMode();
logger.warn(msg);
throw new RuntimeException(msg);

}

}

/**
* Show message box.
*
* In default mode, a native MessageBox is used.
* In testing mode, we use a MessageDialog, showing the same title and message.
*
* @param messageText the text of the message
* @param title the title
* @param iconStyle the icon style
* @param shell the parent shell
*/
public static void showMessageBox(Shell shell, String messageText,
final String title, final int iconStyle) {
if (shell == null) {
logger
.fatal("Shell not yet instantiated, cannot display error message");
} else {
switch (getMode()) {
case DEFAULT: {
MessageBox messageBox = new MessageBox(shell, iconStyle);
messageBox.setMessage(messageText);

messageBox.setText(title);

messageBox.open();
break;
}
case TESTING: {
MessageDialog messagDialog = new MessageDialog(shell, title,
null, messageText, iconStyle, new String[] { "OK" }, 0);
messagDialog.open();
break;
}
default:
final String msg = "Reached default case in NativeDialogFactory,
this is a bug, unknown state "
+ getMode();
logger.warn(msg);
throw new RuntimeException(msg);
}
}
}
}
================== END LISTING =============================================

Within the same package, I placed a basic Input validator, simply
because the dialogs need one
(100% SWT-Boilerplate code ;-)

=================== LISTING DummyInputEvaluator ==============================

import org.eclipse.jface.dialogs.IInputValidator;

/**
* The DummyInputValidator will accept any input.
*
* @author Jan Petranek
*
*/
public class DummyInputValidator implements IInputValidator {

/**
* Always accepts the input.
*
* @param newText
* text to accept
* @return always returns null
* @see org.eclipse.jface.dialogs.IInputValidator#isValid(java.lang.String)
*/
public String isValid(String newText) {

return null;
}
}
================== END LISTING =============================================

The next snippet shows you how you can use the file selection dialog
within your application code

=================== LISTING SaveAsHandler ==============================

public class SaveAsHandler extends AbstractHandler implements IHandler {


public Object execute(ExecutionEvent event) throws ExecutionException {

// File standard dialog
String selected = NativeDialogFactory.fileSelectionDialog(
SingletonHolder.getShell(), "Save as...", SWT.SAVE);

if (selected == null) {
logger.info("File selection aborted");
return null;
}

File file = new File(selected);

// if the file exists, check for writeability

if (!file.exists() || (file.exists() && file.canWrite())) {
// whatever you do with the selected file ...
================== END LISTING =============================================

Last, but not least, a code snippet with the test code:

=================== LISTING MyFirstTest ==============================
@Test
public void testSaveAsUsingMockDialog() throws Exception {
// name of file where to save to
String destinationFile = "/tmp/created.file";

// set the operation mode to testing
NativeDialogFactory.setMode(NativeDialogFactory.OperationMode.TESTING);

bot.menu("File").menu("Save As...").click();

// assert we see the Save As Dialog by checking the title:
assertEquals("Dialog title for Save As Dialog", "Save as...", bot
.activeShell().getText());

// enter the file name
// our factory will use a simple dialog, where we can
// set the file name into the text box
bot.text().setText(destinationFile);

// assert we see an "OK"-Button at position 0 and push it
assertEquals("Button in File Dialog", "OK", bot.button(0).getText());
bot.button(0).click();

// check the message saying that our file was saved
assertEquals("We should see a message saying the file was saved",
"File saved", bot.activeShell().getText());

// assert we see an "OK"-Button at position 0 and push it
assertEquals("Button in Message Dialog", "OK", bot.button(0).getText());
bot.button(0).click();
}
================== END LISTING =============================================

That's it for today, have a nice weekend,

Jan Petranek
Jan Petranek
2009-09-25 20:57:29 UTC
Permalink
Hi, it's me again,

I before you start playing with the code, I better give you some
advice, so you don't run into the same frustrating surprises on your
first attempts.

My project layout:
Three SWT plugin projects take part in this test example:

MockFactory project: This is the project, where the
NativeDialogFactory and the DummyInputValidator reside.
Application project: The application I create (i.e. what the user will get)
Test Project: the SWTBot test project

I exported the MockFactory project as a plugin. I first tried to
import the MockFactory project as a plugin into the Application
project. This however broke my PDE build (it did build, but the
application failed to start). So instead, I took plugin jar-file and
placed it into the application project.

The NativeDialogFactory is desinged as singleton with a "global
variable", the operation mode (remember, in test mode you set it to
TESTING). This works only of course, when there is really only one
NativeDialogFactory around. The application project must export the
mockfactory package to make the NativeDialogFactory visible from the
Test project.

Don't include the MockFactory jar directly into your Test project,
too. In this case, your test code will set the NativeDialogFactory to
test mode - but there seem to be two NativeDialogFactories: One lives
in the jar within the Application project, the other lives in the jar
within the Test project. When the code in each project calls it, its
Classloader will resolve to its own jar.

Hope this posting wasn't too confusing,

Jan Petranek
Jan Petranek
2009-10-18 09:20:43 UTC
Permalink
This Mock-Dialog solution is now available, I even included some
documentation. (The documentation is probable longer than the code
logic ;-)

See:
http://blunt.homelinux.net/wiki/index.php/SWT_Mockdialogs

You can also download it as a jar.
Ketan Padegaonkar
2009-10-20 03:26:58 UTC
Permalink
(Copying the newsgroup for people to try out this cool workaround)

A workaround for mockdialogs in case anyone is interested.

Jan,
Could you file a bug with plugin ?

You'll need to:
1. add the EPL license header to all source files
2. change the namespace to org.eclipse.swtbot from net.sourceforge.*
--
Ketan
http://studios.thoughtworks.com/twist | http://twitter.com/ketanpkr
Post by Jan Petranek
This Mock-Dialog solution is now available, I even included some
documentation. (The documentation is probable longer than the code
logic ;-)
http://blunt.homelinux.net/wiki/index.php/SWT_Mockdialogs
You can also download it as a jar.
_______________________________________________
swtbot-dev mailing list
https://dev.eclipse.org/mailman/listinfo/swtbot-dev
Jan Petranek
2009-10-21 20:14:23 UTC
Permalink
Post by Ketan Padegaonkar
(Copying the newsgroup for people to try out this cool workaround)
A workaround for mockdialogs in case anyone is interested.
Jan,
Could you file a bug with plugin ?
I filed it as Bug # 292953.
Post by Ketan Padegaonkar
1. add the EPL license header to all source files
Already there.
Post by Ketan Padegaonkar
2. change the namespace to org.eclipse.swtbot from net.sourceforge.*
I'll get to it, as soon as I find the time.

How do you want the code? Is a download at my site sufficient or shall
I post it with the bug?

Do you want to include it into SWTBot? Of course, the license allows
this and I would be happy to have the code there...

May your tests be successful,

Jan Petranek
Ketan Padegaonkar
2009-10-22 02:01:26 UTC
Permalink
Please attach everything you wish to contribute on the bugzilla. Don't want
lawyers knocking on anyone's door ;)

Cheers!

--
Ketan
studios.thoughtworks.com | twitter.com/ketanpkr
Sent from my android
(Copying the newsgroup for people to try out this cool workaround) > > A
workaround for mockdialog...
I filed it as Bug # 292953.
You'll need to: > 1. add the EPL license header to all source files
Already there.
2. change the namespace to org.eclipse.swtbot from net.sourceforge.*
I'll get to it, as soon as I find the time.

How do you want the code? Is a download at my site sufficient or shall
I post it with the bug?

Do you want to include it into SWTBot? Of course, the license allows
this and I would be happy to have the code there...

May your tests be successful,

Jan Petranek

_______________________________________________ swtbot-dev mailing list
swtbot-***@eclipse.org https...

Loading...