Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » SWTBot » Page objects for standard views?(Is there any merit in creating standard page objects to access standard Eclipse views)
Page objects for standard views? [message #524854] Fri, 02 April 2010 19:54 Go to next message
Richard Adams is currently offline Richard Adams
Messages: 77
Registered: July 2009
Location: Edinburgh
Member
Hello
I've been reading about the use of 'Page Objects' to provide an API to ui elements and was wondering if it would be useful to create some classes to access standard Eclipse views.
For example in our app we've written a class around the SearchResults view, with methods like:

getNumResults()
getResult(int row)
etc

and we
also need to interrogate the ProgressView as well - if anyone thinks this is possibly useful we can polish them up and make them available.
Cheers
Richard


Re: Page objects for standard views? [message #524966 is a reply to message #524854] Sun, 04 April 2010 18:52 Go to previous messageGo to next message
Ralf Ebert is currently offline Ralf Ebert
Messages: 168
Registered: July 2009
Senior Member
Hi Richard,

> and we also need to interrogate the ProgressView as well - if anyone
> thinks this is possibly useful we can polish them up and make them
> available. Cheers

that sounds like a very good idea! I have also copied the code to create new projects from
time to time, it would be great to have share a common IDE page bundle for these things.

Greetings,

Ralf


--
http://www.ralfebert.de/blog/
http://twitter.com/ralfebert/
Re: Page objects for standard views? [message #525280 is a reply to message #524854] Mon, 05 April 2010 17:36 Go to previous messageGo to next message
Ketan Padegaonkar is currently offline Ketan Padegaonkar
Messages: 873
Registered: July 2009
Senior Member
There's something in a bug[1] that could use some cleanup. Additional
stuff that you have to contribute is also good :)

-- Ketan

[1] - https://bugs.eclipse.org/bugs/show_bug.cgi?id=280641

On 4/2/10 4:54 PM, Richard Adams wrote:
> Hello I've been reading about the use of 'Page Objects' to provide an
> API to ui elements and was wondering if it would be useful to create
> some classes to access standard Eclipse views.
> For example in our app we've written a class around the SearchResults
> view, with methods like:
>
> getNumResults()
> getResult(int row)
> etc
>
> and we also need to interrogate the ProgressView as well - if anyone
> thinks this is possibly useful we can polish them up and make them
> available. Cheers
> Richard
Re: Page objects for standard views? [message #526435 is a reply to message #525280] Sat, 10 April 2010 21:34 Go to previous messageGo to next message
Richard Adams is currently offline Richard Adams
Messages: 77
Registered: July 2009
Location: Edinburgh
Member
Hello
Here (below) is an attempt at a page object for a standard Eclipse search result view or any page implementing the org.eclipse.search.searchResultViewPages
extension point. It's by no means complete (e.g., it could have more convenience methods for analyzing results displayed in a tree).

Interested to hear if this is useful.

Best wishes

Richard
package swtbot.pageobjects;
import org.eclipse.search.ui.text.AbstractTextSearchViewPage;
import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTable;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
import org.eclipse.ui.part.PageBookView;

/**
 * <p>Page object class for a basic search page implementing the  <br/>
 * 
 * <code>org.eclipse.search.searchResultViewPages</code><br/> 
 * 
 * extension point.</p> 
 * <p>
 * In particular this class assumes the search result page extends the 
 * default abstract class: <br/>
 *  <code>org.eclipse.search.ui.text.AbstractTextSearchViewPage</code>
 *  </p>
 *  <p>
 *   One further requirement for using this class is that the table or tree configured in the
 *   method: <br/>
 *   <code> protected abstract void configureTableViewer(TableViewer viewer);<code><br/>
 *   or <br/>
 *   protected abstract void configureTreeViewer(TreeViewer viewer); <br/>
 *   sets an ID for the underlying widget.</p>
 *   <p> For example:<br/>
 *    <code>viewer.getTable().setData("org.eclipse.swtbot.widget.key", "myID");</code>
 *    </p>
 *   <p>
 *    Usage of this class in a test could be as follows :<br/>
 *    <pre>
 *  //... perform a search and wait for search page to appear..
 *
 * SearchResultViewPO view = new SearchResultViewPO(bot, 
 *               BioModelsSearchResultPage.VIEW_WIDGET_ID, "Search");
	assertTrue(view.isFlat());
	assertTrue(view.getNumResults()==2);
	assertTrue(view.getNumColumns()==4);
	</pre>
	</p>
	<p> So this class should only be instantiated when a search view is present.
	This can be established by a simple waitUntil condition. E.g.,
	</p>
	<pre>
	class SearchResultViewOpened extends DefaultCondition {
		private SWTWorkbenchBot bot;
		private String viewTitle;
		public SearchResultViewOpened(SWTWorkbenchBot bot, String viewTitle) {
			this.bot = bot;
			this.viewTitle=viewTitle;
		}
		public String getFailureMessage() {
			return "Search results view could not be opened";
		}

		public boolean test() throws Exception {
			return bot.viewByTitle(viewTitle) != null;
		}
	}
	</pre>
	<p>
	 Eg.,
	 </p>
	 <pre> 
	         //do Search
	         bot.waitUntil(new SearchResultViewOpened());
	        // SearchResultViewPO view = .... as example above.
	 </pre>
	         
 *   
 * @author Richard Adams
 *
 */
public class SearchResultViewPO {
		private final SWTWorkbenchBot bot;
		private final String id;
		private SWTBotView view;
		
        /**
         * Constructor for SearchResultViewPO. Should be called from the test thread.
         * @param bot A non-null {@link SWTWorkbenchBot}
         * @param viewWidgetID A non-null & non-empty <code>String</code>.
         * 		 The ID of either the tree or table widget holding the results.
         * @param viewTitle A <code>String </code> of the view title.
         * @throw {@link IllegalArgumentException} if any argument is null or 
         * 				String arguments  are  empty.
         */
		public SearchResultViewPO (final SWTWorkbenchBot bot, final String viewWidgetID, final String viewTitle) {
			super();
			if(bot==null || viewWidgetID==null || viewWidgetID.equals("")
					|| viewTitle.equals("")){
				throw new IllegalArgumentException();
			}
			this.bot = bot;
			this.id=viewWidgetID;
			view = bot.viewByTitle(viewTitle);
		}
		
		/**
		 * Gets the number of results. For a tree this is the number of top-level results.
		 * For a table, this is the number of rows in the result table.
		 * @return An <code>int</code> number of results.
		 */
		public int getNumResults() {
			if (isTree()){
				return bot.tree().rowCount();
			} else {
				return bot.tableWithId(id).rowCount();
				
			}
		}
		
		/**
		 * Gets the number of columns in the result view. 
		 * @return An <code>int</code> number of columns.
		 */
		public int getNumColumns() {
			if (isTree()){
				return bot.treeWithId(id).columnCount();
			} else {
				return bot.tableWithId(id).columnCount();
				
			}
		}
		
		/**
		 * Retrieves the underlying <code>SWTBotTable</code>. <br/>
		 * This method should only be called for a search result view configured
		 * to be flat. <br/>
		 * I.e., if isFlat() == <code>true</code>
		 * @return The underlying  {@link SWTBotTable}
		 * @throw  {@link IllegalStateException} if <br/>
		 * <code> isFlat() == false </code>
		 */
		public SWTBotTable getSearchResultTable() {
			if (!isFlat()){
				throw new IllegalStateException();
			}
			return bot.tableWithId(id);	
		}
		
		/**
		 * Retrieves the underlying <code>SWTBotTree</code>. <br/>
		 * This method should only be called for a search result view configured
		 * to be a tree. <br/>
		 * I.e., if isTree() == <code>true</code>
		 * @return The underlying  {@link SWTBotTree}
		 * @throw  {@link IllegalStateException} if <br/>
		 *  <code> isTree() == false </code>
		 */
		public SWTBotTree getSearchResultTree() {
			if (!isTree()){
				throw new IllegalStateException();
			}
			return bot.treeWithId(id);	
		}

		/**
		 * Boolean test for whether the search result view is tree-based.
		 * @return <code>true </code> if the search result page is tree based.
		 */
		public boolean isTree(){
			return  getSearchPage().isLayoutSupported(AbstractTextSearchViewPage.FLAG_LAYOUT_TREE);
		}

		/**
		 * Boolean test for whether the search result view is flat (table-based).
		 * @return <code>true </code> if the search result page isflat.
		 */
		public boolean isFlat(){            
			return  getSearchPage().isLayoutSupported(AbstractTextSearchViewPage.FLAG_LAYOUT_FLAT);
		}
		
		
		private AbstractTextSearchViewPage getSearchPage() {
			PageBookView sv = ((PageBookView)(view.getViewReference().getView(false)));
			AbstractTextSearchViewPage isp = (AbstractTextSearchViewPage)sv.getCurrentPage();
			return isp;
		}
		
	}







Re: Page objects for standard views? [message #526801 is a reply to message #524854] Mon, 12 April 2010 20:38 Go to previous message
Richard Adams is currently offline Richard Adams
Messages: 77
Registered: July 2009
Location: Edinburgh
Member
And here is a possible page object for a 'Problems' view - could probably be improved with use of Matchers etc. Plenty more useful methods possible but these are currently sufficient in our tests.
package swtbot.pageobjects;

import static org.junit.Assert.assertTrue;

import java.io.File;

import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot;
import org.eclipse.swtbot.eclipse.finder.widgets.SWTBotView;
import org.eclipse.swtbot.swt.finder.waits.DefaultCondition;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;

import uk.ac.ed.csbe.sbsivisual.conditions.ViewOpened;
import uk.ac.ed.csbe.sbsivisual.test.ProjectUtils;

/**
 *<p> This class is a wrapper around a Problems View for the purposes of testing.
 * It is not particularly intended to be used in testing the Problems View itself, more that
 * it provides a way to ensure that markers are being generated and displayed.
 * 
 * <p>This current version assumes that you have a default Problems view grouped by 'Severity'.I.e.,
 *  the top level items in the view are 'Errors' and 'Warnings'.
 *  
 *  <p>Example usage: 
 *  <pre>
 *  
 *      // here we create 2 new projects, each with a single application specific error
 *      ProjectUtils.createStandardProjectWithTestDataAndHeader("TEST", 
				     new File("TestData/ABC_withMissingHeader.sbsidata"),
				     TestDataUtils.getTestDataHeader(), bot);
		ProjectUtils.createStandardProjectWithTestDataAndHeader("TEST2", 
			     new File("TestData/ABC_withMissingHeader.sbsidata"),
			     TestDataUtils.getTestDataHeader(), bot);
		// test set up					
		final String EXPECTED_WARNING_MESSAGE_FRAGMENT="No header file found";
		
		// open problems view
		ProblemsViewPO problemsPO = ProblemsViewPO.openProblemsView(bot);
		// wait for all markers to appear in the view after running in  a workspace listener.
		problemsPO.waitForAtLeastNofType(ProblemsViewPO.WARNINGS, 2);
		// check correct warnings  are displayed.
		assertTrue(problemsPO.getMessage(0, ProblemsViewPO.WARNINGS).contains(EXPECTED_WARNING_MESSAGE_FRAGMENT));
 *  
 *  </pre>
 *  
 *  
 * @author radams
 *
 */
public class ProblemsViewPO {
	// private constructor used by static factory methods.
	private ProblemsViewPO(SWTWorkbenchBot bot) {
		super();
		this.bot = bot;
	}

	private final SWTWorkbenchBot bot;
	private SWTBotView botView;
	
	public final static String ERROR = "Errors";
	public final static String WARNINGS = "Warnings";

	/**
	 * Factory method which opens the Problems view. This method waits for the problem view to 
	 * appear. Assumes the standard Window->Other->Show View mechanism is used and that the 
	 * view is called 'Problems' ( the default name).
	 * 
	 * @param bot A non-null SWTWorkbenchBot bot.
	 * @return A {@link ProblemsViewPO} object wrapping a Problems view.
	 * @throws Timeout exception if the problems view cannot be opened.
	 */
	public static ProblemsViewPO openProblemsView(final SWTWorkbenchBot bot) {
		return openProblemsView(bot, "Problems");
	}

	/**
	 * Factory method which opens the Problems view. This method waits for the problem view to 
	 * appear. Assumes the standard Window->Other->Show View mechanism is used.
	 * 
	 * @param bot A non-null SWTWorkbenchBot bot.
	 * @param title A <code>String</code> title of the Problems view as it appears in the Window menu.
	 * @return A {@link ProblemsViewPO} object wrapping a Problems view.
	 * @throws Timeout exception if the problems view cannot be opened.
	 */
	public static ProblemsViewPO openProblemsView(final SWTWorkbenchBot bot,
			final String title) {
		ProblemsViewPO viewPO = new ProblemsViewPO(bot);
		viewPO.init(title);
		return viewPO;
	}

	private void init(final String title) {
		bot.menu("Window").menu("Other...").click();
		bot.shell("Show View").activate();
		bot.tree().expandNode("General").expandNode(title).doubleClick();
		bot.waitUntil(new ViewOpened(bot, title)); // simple Condition class to test for view
		botView = bot.viewByTitle(title);
	}

	/**
	 * Getter for underlying SWTBotView of this problems view.
	 * 
	 * @return the {@link SWTBotView} wrapped by this class. Will not be null if this object
	 *  has initialized properly.
	 */
	public SWTBotView getBotView() {
		return botView;
	}

	/**
	 * Getter for number of displayed warnings
	 */
	public int getDisplayedWarningCount() {
		return getCountofType("Warnings");
	}

	/**
	 * Getter for number of displayed errors
	 */
	public int getDisplayedErrorCount() {
		return getCountofType("Errors");
	}
	
	/**
	 * Gets the String error/warning message for a problem at a specified index.
	 * @param rowNumber the index of the warning message.
	 * @param type 
	 * @return The message, or the empty string if no problems of the spcified type found
	 * @throws IndexOutOfBoundsException if rowNumber does not exist ( zero -based).
	 */
	public String getMessage(int rowNumber, String type) {
		final int MESSAGE_COLUMN=0;
		SWTBotTree tree = botView.bot().tree();
		SWTBotTreeItem[] items = tree.getAllItems();
		for (SWTBotTreeItem item : items) {
			if (item.getText().contains(type)) {
				return item.expand().getItems()[rowNumber]
				     		               .row().get(MESSAGE_COLUMN);
			}
		}
		return "";
	}
	
	/**
	 * Waits until a given number of errors or warnings are displayed.
	 * This method is useful if you have modified a resource and want to check the markers
	 * are updated. Currently this just works for number of displayed markers, rather than all markers. 
	 * This is not a test for an exact number of problems 
	 * @param problemType One of ProblemsViewPO.ERROR or ProblemsViewPO.Warnings
	 * @param expected An <code> int </code> of the number of expected errors or warnings, >=0
	 * @throws IllegalArgumentException if <code> expected < 0</code>, or problem type is not a valid String
	 */
	public void waitForAtLeastNofType (String  problemType, int expected) {
		if (expected < 0 || (!checkType(problemType))) {
			throw new IllegalArgumentException();
		}
		bot.waitUntil(new NAreGenerated(expected, problemType));
	}

	private boolean checkType(String problemType) {
		return problemType.equals(ProblemsViewPO.ERROR) || 
				            problemType.equals(ProblemsViewPO.WARNINGS);
	}

	private int getCountofType(String type) {
		SWTBotTree tree = botView.bot().tree();
		SWTBotTreeItem[] items = tree.getAllItems();
		for (SWTBotTreeItem item : items) {
			if (item.getText().contains(type)) {
				return item.expand().getItems().length;
			}
		}
		return 0;
	}
	
	/*
	 * Condition class which tests for n warnings or errors being in the P
	 */
	class NAreGenerated extends DefaultCondition {
        private int expected=0;
        private String type;
		private NAreGenerated(int expected,  String type) {
			super();
			this.expected = expected;
		
			this.type=type;
		}
		
		public String getFailureMessage() {
			return "Could not find " + expected + " problems of type [" + type + "]";
		}
		public boolean test() throws Exception {
			return getCountofType(type) == expected;
		}
	}

/**
* General condition that a view with a given title has opened.
*/
public class ViewOpened extends DefaultCondition {
	private SWTWorkbenchBot bot;
	private String viewTitle;
	public ViewOpened(SWTWorkbenchBot bot, String viewTitle) {
		this.bot = bot;
		this.viewTitle=viewTitle;
	}
	public String getFailureMessage() {
		return "View could not be opened";
	}

	public boolean test() throws Exception {
		return bot.viewByTitle(viewTitle) != null;
	}
}
	

}






Previous Topic:New to SWTBot - Need help on from where to start
Next Topic:CTRL+mouseClick
Goto Forum:
  


Current Time: Sat Apr 19 00:34:04 EDT 2014

Powered by FUDForum. Page generated in 0.15980 seconds