Java Developer Tools

Creating a Custom Locator (SWT)

For most applications, the locators shipped with WindowTester Pro are more than sufficient. That said, it can be convenient at times to create custom locators of your own. To start, you should be familiar with how widget finding works.

  1. Creating a Custom Locator (SWT)
  2. Why Create a Custom Locator?
    1. Overriding Matching Criteria
    2. Supporting Custom Widgets
  3. Extended Example: Creating a Custom Browser Locator


Why Create a Custom Locator?

Reasons to create a custom locator include:

  1. You want to override the default matching criteria of a standard locator
  2. You want to create a locator for an unsupported widget

Overriding Matching Criteria

Overriding matching criteria is done by providing a specialized implementation of the locator’s IWidgetMatcher. In the SWT case the matcher is specified in the SWTWidgetLocator.buildMatcher() method. For example, suppose we wanted to create a custom locator that matched selected buttons. To do this we could start first with a matcher defined like this.

class SelectedButtonMatcher implements IWidgetMatcher {
			
	private final IWidgetMatcher defaultMatcher;

	SelectedButtonMatcher(IWidgetMatcher defaultMatcher) {
		this.defaultMatcher = defaultMatcher;
	}
		
	public boolean matches(Object widget) {
		return isSelected(widget) && matchesDefaultCriteria(widget);
	}

	private boolean matchesDefaultCriteria(Object widget) {
		return defaultMatcher.matches(widget);
	}

	private boolean isSelected(Object widget) {
		if (!(widget instanceof Button))
			return false;
		final Button button = (Button)widget;
		final boolean[] result = new boolean[1];
		Display.getDefault().syncExec(
			new Runnable() {
				public void run() {
					result[0] = button.getSelection(); }							
		});
		return result[0];
	}					
}

(Note that here we are decorating the default matcher. A simpler implementation would forego a reference to the default matcher.)

With this matcher defined a SelectedButtonLocator could be defined like this:

public class SelectedButtonLocator extends ButtonLocator {		
   	private static final long serialVersionUID = 1L;

	public SelectedButtonLocator(String label) {
      		super(label);
   	}
		
	@Override
	protected IWidgetMatcher buildMatcher() {
		IWidgetMatcher defaultMatcher = super.buildMatcher();
		return new SelectedButtonMatcher(defaultMatcher);
	}			
}

Using this locator looks just like using the standard button locator:

ui.click(new ButtonLocator("OK")); //standard
ui.click(new SelectedButtonLocator(".*")); //custom

(Notice that you can use a wildcard just like the standard locator since we are decorating the default matching criteria. The custom locator reference reads: "click the selected button with any text".)

Supporting Custom Widgets

The simplest kind of custom widget support involves creating a convenience subclass of SWTWidgetLocator which adds no behavior. For example, the following locator matches eclipse forms UI Section widgets.

public class SectionLocator extends SWTWidgetLocator {

	private static final long serialVersionUID = 621335057837701982L;

	public SectionLocator(String text) {
		super(Section.class, text);
	}

	public SectionLocator(String text, SWTWidgetLocator parent) {
		super(Section.class, text, parent);
	}

	public SectionLocator(String text, int index, SWTWidgetLocator parent) {
		super(Section.class, text, index, parent);
	}
}

Notice that this is essentially the same as the first case where we are specifying custom matching criteria. Here we are taking advantage of SWTWidgetLocator's default strategy of matching on widgets by class.

(More complex are widgets whose selection implementation needs to be customized. For example, a widget whose components need to be revealed to be clicked, such as a tree or tray, will require more than the default SWTWidgetLocator click handling. In a future article we will cover how to provide custom selection logic for such a widget.)

Extended Example: Creating a Custom Browser Locator

To highlight some of the subtleties of creating locators (and more of the power), the following example presents a more interesting kind of locator, for the org.eclipse.swt.browser.Browser widget. (Note: the latest source for this example can be found on the WindowTester Pro Commons site here.)

The browser is interesting because it contains rendered HTML. In this example we’d like to create a browser locator that provides some access to that HTML so that we can use it in making assertions. When we’re done we will be able to write things like this:

ui.assertThat(new BrowserLocator().htmlContains(EXPECTED_URL));

The trick in doing this is to create a special kind of IWidgetReference for browser instances. This will be returned from IUIContext.findAll(..) calls. Our BrowserLocator looks like this:

public class BrowserLocator extends SWTWidgetLocator {

	private static final long serialVersionUID = 1L;
	public BrowserLocator() {
		super(Browser.class);
	}
	@Override
	public IWidgetLocator[] findAll(IUIContext ui) {
		IWidgetLocator[] refs = super.findAll(ui);
		BrowserReference[] browsers = new BrowserReference[refs.length];
		for (int i = 0; i < browsers.length; i++) {
			browsers[i] = new BrowserReference((IWidgetReference)refs[i]);
		}
		return browsers;
	}
}

Where our BrowserReference is written like this:

public class BrowserReference implements IWidgetReference {
	
	private class HtmlContainsCondition implements ICondition {

		String expectedText;
		
		public HtmlContainsCondition(String expectedText) {
			this.expectedText = expectedText;
		}
			
		public boolean test() {
			String html = getHTML();
			if (html == null)
				return false;
			return html.contains(expectedText);
		}				
	}

		
	private final IWidgetReference browserWidget;

	public BrowserReference(IWidgetReference browserWidget) {
		this.browserWidget = browserWidget;
	}
				
	public Object getWidget() {
		return browserWidget.getWidget();
	}

	/**
	  * @since Eclipse 3.4 where Browser.getText() is introduced
	  */
	public String getHTML() {
		final String text[] = new String[1];
		Display.getDefault().syncExec(new Runnable() {
			public void run() {
				text[0] = getText(getBrowser());
			}			
		});
		return text[0];
	}
		
	private static String getText(Browser browser) {
		if (browser == null)
			return null;
			
		try {
			Method m = browser.getClass().getMethod("getText", (Class[]) null);
			m.setAccessible(true);
			return (String) m.invoke(browser, (Object[]) null);
		} catch (SecurityException e) {
		} catch (NoSuchMethodException e) {
		} catch (IllegalArgumentException e) {
		} catch (IllegalAccessException e) {
		} catch (InvocationTargetException e) {
		}
		return null;
		}
		
		public void execute(final String script) {
			Display.getDefault().syncExec(new Runnable() {
				public void run() {
					getBrowser().execute(script);
				}			
			});
		}
		public void setURL(final String url) {
			Display.getDefault().syncExec(new Runnable() {
				public void run() {
					getBrowser().setUrl(url);
				}			
			});
		}
		
		public ICondition htmlContains(String expectedText) {
			return new HtmlContainsCondition(expectedText);
		}
		
		public Browser getBrowser() {
			return (Browser)getWidget();
		}
		
		/* (non-Javadoc)
		 * @see com.windowtester.runtime.locator.IWidgetLocator#findAll(com.windowtester.runtime.IUIContext)
		 */
		public IWidgetLocator[] findAll(IUIContext ui) {
			return browserWidget.findAll(ui);
		}

		/* (non-Javadoc)
		 * @see com.windowtester.runtime.locator.IWidgetMatcher#matches(java.lang.Object)
		 */
		public boolean matches(Object widget) {
			return browserWidget.matches(widget);
		}
		
		
	}

  
  

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.