package ca.ucalgary.seahawk.gui.test;

import ca.ucalgary.seahawk.gui.*;
import ca.ucalgary.seahawk.util.*;

import org.biomoby.client.CentralImpl;
import org.biomoby.shared.Central;
import org.biomoby.shared.CentralCached;
import org.biomoby.shared.MobyDataType;
import org.biomoby.shared.data.*;

import junit.framework.*;
import junit.extensions.jfcunit.*;
import junit.extensions.jfcunit.finder.*;
import junit.extensions.jfcunit.eventdata.*;

import javax.swing.*;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Vector;

/**
 * Tests all of the major functionality of Seahawk, the standalone MOBY
 * browser that can be embedded in other applications too. If you add classes
 * to Seahawk, you MUST test them here, otherwise they will not end up in the 
 * Seahawk deployment JAR.
 */

public class SeahawkTestCase extends JFCTestCase{
    private final static String TEST_MS_WORD_FILE = "ca/ucalgary/seahawk/gui/test/perlregex.doc";
    private final static String TEST_MS_EXCEL_FILE = "ca/ucalgary/seahawk/gui/test/twohybrid_uetz.xls";
    private final static String TEST_MOBYEX_XML = "ca/ucalgary/seahawk/gui/test/moby_exception.xml";
    private final static String TEST_MOBY_XML = "ca/ucalgary/seahawk/gui/test/allDataTypes.xml";
    private final static String TEST_WSDL_FILE = "http://www.ncbi.nlm.nih.gov/entrez/eutils/soap/v2.0/efetch_seq.wsdl";
    private final static String TEST_EXTERNAL_URL = "http://www.google.com/";
    private final static String TEST_DNA_SEQ = "AAGCTTGGCCAACGTAAATCTTTCGGCGGCA";
    private final static String TEST_AA_SEQ = "MPGGFILAIDEGTTSARAIIYNQDLEVLGIGQYDFPQHYPSP";

    private JFCTestHelper helper = null;

    private MobyContentGUI contentGUI;
    private NamedComponentFinder finder;

    /**
     * @param name Test case name.
     */
    public SeahawkTestCase(String name) {
        super(name);
	//System.setProperty("seahawk.rules", "docs/simpleTestBuilderRules.xml");
    }

    /**
     * Sets up the test fixture.
     * Called before every test case method.
     */
    protected void setUp() throws Exception{
	super.setUp();

	setHelper(new JFCTestHelper()); // Uses the AWT Event Queue

	contentGUI = MobyUtils.getMobyContentGUI(new JLabel());
	contentGUI.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

	Finder.setDefaultWait(3);
        finder = new NamedComponentFinder(JComponent.class, "");
	contentGUI.setVisible(true);
    }

    protected void tearDown() throws Exception{
        contentGUI = null;
	MobyUtils.clearGUIs();// gets rid of static references
        getHelper().cleanUp(this);
        super.tearDown();
    }

    // Tests tab close functionality too
    public void testHelpTab(){

	// Show the help tab programmatically
	int helpTabIndex = contentGUI.showHelpTab();
	finder.setName(MobyContentHelpPane.HELP_TAB_NAME);
	Object foundObj = finder.find(contentGUI, 0);

	assertNotNull("The Help tab could not be programmatically loaded", foundObj);
	assertTrue("Help tab found, but was not a MobyContentHelpPane as expected", 
		   foundObj instanceof MobyContentHelpPane);
	String contents = ((MobyContentHelpPane) foundObj).getHTMLSource();
	assertTrue("Help tab (loaded programmatically) could not load any contents (text is null)", 
		   contents != null);
	assertTrue("Help tab (loaded programmatically) contents was zero-length, " +
		   "but we expected at least some help text", 
		   contents.length() > 0);

	// Kill the help tab by faking the required action event
	contentGUI.setActiveTab(helpTabIndex);
	contentGUI.actionPerformed(new ActionEvent(this, 0, MobyContentGUI.CLOSE_TAB_OPTION));
	foundObj = finder.find(contentGUI, 0);
	assertNull("Could not close the help tab by sending MobyContentGUI " +
		   "a MobyContentGUI.CLOSE_TAB_OPTION ActionEvent command", foundObj);
    }

    // Assume that testHelpTab has been run first
    public void testHelpButton(){

	// Simulate the user pressing the help button
	finder.setName(MobyContentGUI.HELP_BUTTON_NAME);
	JButton helpButton = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The help button is missing from the Seahawk interface", helpButton);
	getHelper().enterClickAndLeave( new MouseEventData( this, helpButton ) );
	
	// Check that everything's okay this way too
	finder.setName(MobyContentHelpPane.HELP_TAB_NAME);
	Object foundObj = finder.find(contentGUI, 0);
	assertNotNull("The Help tab could not be loaded via the help button " +
		      "(but was okay when loaded programmatically)", foundObj);
	assertTrue("Help tab loaded via help button, but was not a MobyContentHelpPane as expected " +
		   "(but was okay when loaded programmatically)", 
		   foundObj instanceof MobyContentHelpPane);
	String contents = ((MobyContentHelpPane) foundObj).getHTMLSource();
	assertTrue("Help tab loaded via help button could not load any contents (text is null, " + 
		   "but was okay when loaded programmatically)", 
		   contents != null);
	assertTrue("Help tab (loaded via help button) contents was zero-length, " +
		   "but we expected at least some help text (was okay when loaded programmatically)", 
		   contents.length() > 0);
    }

    // Load a MOBY document, then add parts of it to the clipboard
    // Note that the tests are programmatic via the API, not a GUI interaction, as this
    // is very hard to do given the popup menu dynamics of Seahawk
    public void testClipboardTab(){
	// make sure the clipboard is blank at the start
	MobyDataObjectSet clipSet = contentGUI.addToClipboard(null);
	assertNotNull("The clipboard is null, but should be initialized with the Seahawk display", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + 
		   ") of items, expected 0 at the start", 
		   clipSet.size() == 0);

	// test loading two objects with the same type (DNASequence) , programmatically
	MobyDataObject mobyObject = MobyUtils.createMobySequence(TEST_DNA_SEQ, "foo");
	clipSet = contentGUI.addToClipboard(mobyObject);
	assertNotNull("The clipboard is null, but should be initialized, with one object", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + ") of items, expected 1", 
		   clipSet.size() == 1);

	MobyDataObject mobyObject2 = MobyUtils.createMobySequence("catatcgagtcgttgtgca", "bar");
	clipSet = contentGUI.addToClipboard(mobyObject2);
	assertNotNull("The clipboard is null, but should be initialized, with two objects", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + ") of items, expected 2", 
		   clipSet.size() == 2);
	MobyDataType dnaDataType = MobyDataType.getDataType("DNASequence");
	assertEquals("Shared data type of the two items on clipboard (" + clipSet.getDataType().getName() + 
		     ") was not " + dnaDataType.getName() + " as expected", 
		     dnaDataType, clipSet.getDataType());

	// add an object of another type (AminoAcidSequence)
	MobyDataObject mobyObject3 = MobyUtils.createMobySequence(TEST_AA_SEQ, "baz");
	clipSet = contentGUI.addToClipboard(mobyObject3);
	assertNotNull("The clipboard is null, but should be initialized, with two objects", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + ") of items, expected 3", 
		   clipSet.size() == 3);
	MobyDataType seqDataType = MobyDataType.getDataType("GenericSequence");
	assertEquals("Shared data type of the three items on clipboard (" + clipSet.getDataType().getName() + 
		     ") was not " + seqDataType.getName() + " as expected", 
		     seqDataType, clipSet.getDataType());

	// remove the third object and make sure type is restored
	assertTrue("The third object in the clipboard could not be deleted programmatically", 
		   contentGUI.removeFromClipboard(mobyObject3));
	assertEquals("Shared data type of the two items on clipboard (" + clipSet.getDataType().getName() + 
		     ") was not " + dnaDataType.getName() + " as expected (restoration of data type after " +
		     "item deletion failed)", 
		     dnaDataType, clipSet.getDataType());

	// make sure clearing works
	contentGUI.clearClipboard();
	clipSet = contentGUI.addToClipboard(null);
	assertNotNull("The clipboard is null, but should be initialized with the Seahawk display", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + 
		   ") of items, expected 0 at the start", 
		   clipSet.size() == 0);

	// add an object back
	clipSet = contentGUI.addToClipboard(mobyObject2);
	assertNotNull("The clipboard is null, but should be initialized, with one object", clipSet);
	assertTrue("The clipboard contains the wrong number (" + clipSet.size() + ") of items, expected 1", 
		   clipSet.size() == 1);
	assertEquals("Shared data type of the item on clipboard (" + clipSet.getDataType().getName() + 
		     ") was not " + dnaDataType.getName() + " as expected (restoration of data type after " +
		     "clipboard clearing failed)", 
		     dnaDataType, clipSet.getDataType());
	
    }

    public void testSave() throws Exception{
	finder.setName(MobyContentGUI.SAVE_BUTTON_NAME);
	final JButton save = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The save button is missing from the Seahawk interface", save);

	MobyDataObject mobyObject = MobyUtils.createMobySequence(TEST_DNA_SEQ, "foo");
	contentGUI.loadPaneFromObject(new MobyContentInstance(mobyObject, "save-test"), true);  //true = load in new tab

	// Check that file option launches dialog
	new Thread(){public void run(){save.doClick();}}.start();
	sleep(2000);
	finder.setName(MobySaveDialog.FILE_CHOOSER_SAVE_TITLE);
	List showingDialogs = finder.findAll();
	assertTrue("Number of file choosers with name \""+MobySaveDialog.FILE_CHOOSER_SAVE_TITLE+
		   "\" showing is wrong: " + showingDialogs.size() + " (should be 1)", 
		   showingDialogs.size() == 1);
	JFileChooser fc = (JFileChooser) showingDialogs.get(0);

	// Write a file to the default temporary directory
	File testFile = null;
	try{
	    File homeDir = new File(System.getProperty("user.home")); 
	    // write to the home dir because file dialog has problems switching dirs
	    testFile = File.createTempFile("test-seahawk", "", homeDir);	    
	    testFile.deleteOnExit();
	    sleep(5000);
	    fc.setSelectedFile(testFile);
	    sleep(1000);
	    fc.approveSelection();
	    //assertTrue("File (" + testFile + ") was not saved, zero-sized", testFile.length() != 0);
	}
	catch(java.io.IOException ioe){
	    System.err.println("Warning: Skipping save-to-disk test, could not write a temporary file:" + ioe);
	    fc.cancelSelection();
	}
	assertNull("File chooser for open operation did not disappear after approval", finder.find());
	
	if(testFile == null){
	    return; //can't test the rest
	}

	// Later on we will want to make thorough tests (in another class maybe?)
	// of the various export options, checking the file contents.
	sleep(2000);
	MobySaveDialog.exportXML(contentGUI.getCurrentPane(), testFile);
	sleep(2000);
	assertTrue("The XML saved data file was empty", testFile.length() != 0);
	MobySaveDialog.exportHTML(contentGUI.getCurrentPane(), testFile);
	sleep(2000);
	assertTrue("The HTML saved data file was empty", testFile.length() != 0);
    }

    public void testPrint(){
	DialogFinder dfinder = new DialogFinder(".*");
	List showingDialogs = dfinder.findAll();
	int numInitialDialogs = showingDialogs.size();

	finder.setName(MobyContentGUI.PRINT_BUTTON_NAME);
	final JButton print = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The print button is missing from the Seahawk interface", print);

	contentGUI.showHelpTab();

	// For rather complicated reasons related to the Service Provider Interface
	// implementation in Java (sun.misc.Service), we cannot load the printer interface
	// here except with the default system ClassLoader.  Because this test is used
	// with the Minnow classloader to build a Seahawk JAR automatically, we must
	// temporarily set the class loader to the system one.  We'll reset it when 
	// the print test is done so Minnow catches the rest of the application.
	ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
	Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());

	new Thread(){public void run(){print.doClick();}}.start();
	sleep(2000);
	showingDialogs = dfinder.findAll();
	// See that a new dialog came up, then dismiss it.  It's about the best we can do at the moment
	assertTrue("No dialog appeared when the print button was pressed", 
		   showingDialogs.size() == numInitialDialogs+1);	
	// get rid of the print window
	getHelper().disposeWindow((java.awt.Dialog) showingDialogs.get(showingDialogs.size()-1), this);

	Thread.currentThread().setContextClassLoader(oldClassLoader);
    }

    public void testNavigationButtons() throws Exception{
	finder.setName(MobyContentGUI.BACK_BUTTON_NAME);
	JButton back = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The back button is missing from the Seahawk interface", back);

	finder.setName(MobyContentGUI.FORWARD_BUTTON_NAME);
	JButton forward = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The forward button is missing from the Seahawk interface", forward);

	assertFalse("The back button is enabled, but there is no history for the tab", back.isEnabled());
	assertFalse("The forward button is enabled, but there is no history for the tab", forward.isEnabled());

	// Load a first document
	URL testMobyURL = getClass().getClassLoader().getResource(TEST_MOBY_XML);
	assertNotNull("Could not find the MOBY test XML document for Seahawk (" + TEST_MOBY_XML + ")",
		      testMobyURL);
	contentGUI.loadPaneFromURL(testMobyURL, true); //true == open in new tab
	assertFalse("The back button is enabled, but there is only one (initial) " +
		    "document in the history for the tab", 
		    back.isEnabled());
	assertFalse("The forward button is enabled, but there is only one (initial) " + 
		    "document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL, contentGUI.getCurrentURL());
	sleep(2000);

	// load a second document
	URL testMobyURL2 = new URL(TEST_EXTERNAL_URL);
	assertNotNull("Could not find the MOBY test XML document for Seahawk (" + TEST_EXTERNAL_URL + ")",
		      testMobyURL2);
	contentGUI.loadPaneFromURL(testMobyURL2, false); // false == reuse tab
	sleep(2000);
	assertTrue("The back button is not enabled, but there is a previous document in the history for the tab", 
		    back.isEnabled());
	assertFalse("The forward button is enabled, but there is no forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL2 + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL2, contentGUI.getCurrentURL());

	back.doClick();
	sleep(2000);
	assertFalse("The back button is enabled, but there is no previous document in the history for the tab", 
		    back.isEnabled());
	assertTrue("The forward button is dinabled, but there is a forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL to be reloaded via back button (" + testMobyURL + 
		    ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL, contentGUI.getCurrentURL());

	forward.doClick();
	sleep(2000);
	assertTrue("The back button is not enabled, but there is a previous document in the history for the tab", 
		    back.isEnabled());
	assertFalse("The forward button is enabled, but there is no forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL2 + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL2, contentGUI.getCurrentURL());

	// load a third document
	URL testMobyURL3 = getClass().getClassLoader().getResource(TEST_MOBYEX_XML);
	assertNotNull("Could not find the MOBY test XML document for Seahawk (" + TEST_MOBYEX_XML + ")",
		      testMobyURL3);
	contentGUI.loadPaneFromURL(testMobyURL3, false); // false == reuse tab
	sleep(2000);
	assertTrue("The back button is not enabled, but there is a previous document in the history for the tab", 
		   back.isEnabled());
	assertFalse("The forward button is enabled, but there is no forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL to be reloaded via back button (" + testMobyURL3 + 
		    ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL3, contentGUI.getCurrentURL());

	back.doClick();
	sleep(2000);
	assertTrue("The back button is not enabled, but we are in the middle of the history for the tab", 
		    back.isEnabled());
	assertTrue("The forward button is not enabled, but we are in the middle of the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL2 + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		     testMobyURL2, contentGUI.getCurrentURL());

	forward.doClick();
	sleep(2000);
	assertTrue("The back button is not enabled, but there is a previous document in the history for the tab", 
		    back.isEnabled());
	assertFalse("The forward button is enabled, but there is no forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL3 + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL3, contentGUI.getCurrentURL());

	back.doClick();
	sleep(2000);
	back.doClick();
	sleep(2000);
	assertFalse("The back button is enabled, but there is no previous document in the history for the tab", 
		    back.isEnabled());
	assertTrue("The forward button is disabled, but there is a forward document in the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL to be reloaded via back button (" + testMobyURL + 
		    ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL, contentGUI.getCurrentURL());

	forward.doClick();
	sleep(2000);
	assertTrue("The back button is not enabled, but we are in the middle of the history for the tab", 
		    back.isEnabled());
	assertTrue("The forward button is not enabled, but we are in the middle of the history for the tab", 
		    forward.isEnabled());
	assertEquals("URL loaded (" + testMobyURL2 + ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		     testMobyURL2, contentGUI.getCurrentURL());
	
	// load a 4th document which should truncate the history
	contentGUI.loadPaneFromURL(testMobyURL, false); // false == reuse tab
	sleep(2000);
	assertTrue("The back button is not enabled, but should be in truncated history for the tab", 
		   back.isEnabled());
	assertFalse("The forward button is enabled, but should not be in truncated history for the tab", 
		    forward.isEnabled());
	assertEquals("URL to be reloaded via back button (" + testMobyURL + 
		    ") and current URL of Seahawk display (" +
		    contentGUI.getCurrentURL() +") are not the same", 
		    testMobyURL, contentGUI.getCurrentURL());
	
    }

    public void testOpenButton() throws Exception{
	finder.setName(MobyContentGUI.OPEN_BUTTON_NAME);
	JButton openButton = (JButton) finder.find(contentGUI, 0);
	assertNotNull("The file/URL open button is missing from the Seahawk interface", openButton);

	// Simulate clicking the open button
	//getHelper().enterClickAndLeave(new MouseEventData(this, openButton));
	openButton.doClick();
	sleep(500);
	finder.setName(MobyContentGUI.OPEN_OPTION_NAME);
	Object openPopup = finder.find();
	assertNotNull("Clicking the file/URL open button did not open the options popup (was null)", 
		      openPopup);
	assertTrue("Component launched by clicking open button was not a JPopupMenu as expected (was a " + 
		   openPopup.getClass().getName(),
		   openPopup instanceof JPopupMenu);
	
	finder.setName(MobyContentGUI.FILE_OPEN_OPTION_NAME);
	//Object openFileItem = finder.find(contentGUI, 0);
	//Object openFileItem = (new JMenuItemFinder(MobyContentGUI.FILE_OPEN_OPTION_NAME)).find(contentGUI, 0);
	final Component openFileItem = getSubComponentByName((JPopupMenu) openPopup, 
							     MobyContentGUI.FILE_OPEN_OPTION_NAME);
	assertNotNull("The document open popup menu did not contain the file open option (was null)", 
		      openFileItem);
	assertTrue("File Option in 'Open Document' JPopupMenu was not a JMenuItem as expected (was a " + 
		   openFileItem.getClass().getName()+")",
		   openFileItem instanceof JMenuItem);

	// Check that file option launches dialog
	new Thread(){public void run(){((JMenuItem) openFileItem).doClick();}}.start();
	sleep(2000);
	finder.setName(MobyContentGUI.FILE_CHOOSER_OPEN_TITLE);
	List showingDialogs = finder.findAll();
	assertTrue("Number of file choosers with title \""+MobyContentGUI.FILE_CHOOSER_OPEN_TITLE+
		   "\" showing is wrong: " + showingDialogs.size() + " (should be 1)", 
		   showingDialogs.size() == 1);
	JFileChooser fc = (JFileChooser) showingDialogs.get(0);
	URL testMobyURL = getClass().getClassLoader().getResource(TEST_MOBY_XML);
	assertNotNull("Could not find the MOBY test XML document for Seahawk (" + TEST_MOBY_XML + ")",
		      testMobyURL);

	// Check that we can programmatically open a MOBY XML file
	finder.setName(MobyContentGUI.TABBED_PANE_NAME);
	JTabbedPane tabbedPane = (JTabbedPane) finder.find(contentGUI, 0);
	assertNotNull("Could not find the tabbed pane for displaying documents in Seahawk (was null)", 
		      tabbedPane);
	int startingTabCount = tabbedPane.getTabCount();

	// Skip the test below until JFileChooser stops flaking out.
// 	if("file".equals(testMobyURL.getProtocol())){
// 	    sleep(5000);
// 	    File testFile = new File(testMobyURL.toURI());
// 	    fc.setCurrentDirectory(testFile.getParentFile());
// 	    sleep(5000);
// 	    fc.setSelectedFile(testFile);
// 	    sleep(5000);
// 	    fc.approveSelection();
// 	}
// 	else{
// 	    System.err.println("Skipping file open dialog test because sample resource is not a local file (" +
// 			       testMobyURL + ")");
// 	    // run programmatically instead
// 	    contentGUI.loadPaneFromURL(testMobyURL, true); //true == open in new tab
 	    fc.cancelSelection();
// 	}
	finder.setName(MobyContentGUI.FILE_CHOOSER_OPEN_TITLE);
	assertNull("File chooser for open operation did not disappear after cancellation", finder.find());

	assertTrue("New tab was not opened for loaded URL", tabbedPane.getTabCount() == startingTabCount+1); // new tab

	contentGUI.loadPaneFromURL(new URL("file:///nonsense-should-fail-dont-worry"), false); 
	assertTrue("New tab was not opened for loaded URL", tabbedPane.getTabCount() == startingTabCount+1);

	contentGUI.loadPaneFromURL(testMobyURL, true); //true == open in new tab
	assertTrue("New tab was not opened for loaded URL", tabbedPane.getTabCount() == startingTabCount+2);

	// Check that opening a Web page works too 
	finder.setName(MobyContentGUI.WEB_OPEN_OPTION_NAME);
	final Component webFileItem = getSubComponentByName((JPopupMenu) openPopup, 
							    MobyContentGUI.WEB_OPEN_OPTION_NAME);
	assertNotNull("The document open popup menu did not contain the file open option (was null)", 
		      webFileItem);
	assertTrue("File Option in 'Open Document' JPopupMenu was not a JMenuItem as expected (was a " + 
		   webFileItem.getClass().getName(),
		   webFileItem instanceof JMenuItem);
	
	new Thread(){public void run(){((JMenuItem) webFileItem).doClick();}}.start();

	DialogFinder dfinder = new DialogFinder(".*"); //MobyContentGUI.WEB_ADDR_DIALOG_TITLE);
	showingDialogs = dfinder.findAll();
	assertTrue("Number of dialogs with title \""+MobyContentGUI.WEB_ADDR_DIALOG_TITLE+
		   "\" showing is wrong: " + showingDialogs.size() + " (should be 1)", 
		   showingDialogs.size() == 1);
	JDialog dialog = (JDialog) showingDialogs.get(0);
	assertTrue("Dialogs title was not \""+MobyContentGUI.WEB_ADDR_DIALOG_TITLE+
		   "\", but rather : " + dialog.getTitle(), 
		   MobyContentGUI.WEB_ADDR_DIALOG_TITLE.equals(dialog.getTitle()));
	
	JTextField urlField = (JTextField) getSubComponentByClass(dialog.getContentPane(), 
				    	  getClass().getClassLoader().loadClass("javax.swing.JTextField"));

	assertNotNull("The Web open dialog did not contain a JTextField as expected (was null)", 
		      urlField);

	getHelper().sendString(new StringEventData(this, urlField, TEST_EXTERNAL_URL));
	getHelper().sendKeyAction(new KeyEventData(this, urlField, java.awt.event.KeyEvent.VK_ENTER));
	//assertNull("Dialog for Web open operation did not disappear after cancellation", finder.find());
	// If loading Google didn't work, we'd expect an Exception of some sort here that JUnit will catch
    }

    // Checks to make sure the Moby-wrapping of WSDL forms works fine
    public void testWSDL() throws Exception{
	URL wsdlResource = TEST_WSDL_FILE.startsWith("http://") ? new URL(TEST_WSDL_FILE) : getClass().getClassLoader().getResource(TEST_WSDL_FILE);
	assertNotNull("Could not find test WSDL resource " + TEST_WSDL_FILE, wsdlResource);
	contentGUI.loadPaneFromURL(wsdlResource, true);
	try{
	    Thread.sleep(10000);
	} catch(Exception e){
	    System.err.println("Sleep interrupted while waiting for browser launch");
	}
	
	System.err.println("Press enter when your Daggoo test is complete");
	int ch = ' ';
	while(ch != '\n'){
	    ch = System.in.read();
	}

	// Kill the servlet engine so that the tests will actually exit!
	try{
	    contentGUI.stopServletContainer();
	} catch(Exception e){
	    System.err.println("I/O problem while stoping servlet container:");
	    e.printStackTrace();
	}
    }

    /** Checks that SeahawkTransferable works */
    public void testDragnDrop() throws Exception{
	// Create some data to drag
	Point linkPos = loadSeqAndLink(TEST_AA_SEQ, "SequenceString");

	// Create a drop location
	JTextField dropField = new JTextField(30);
	JFrame frame = new JFrame("Drop Test Component");
	frame.add(dropField);
	frame.pack();
	frame.setVisible(true);
	Point mainPos = contentGUI.getLocationOnScreen();
	frame.setLocation(mainPos.x+contentGUI.getWidth()+1, mainPos.y); //just right of the main window
	sleep(2000);   

	Point dropPos = null;
	try{
	    dropPos = dropField.getLocationOnScreen();
	} catch(IllegalComponentStateException icse){
	    fail("Exception while getting screen location of drop field in drag event test: " + icse);
	}

	Robot robot = null;
	try{
	    robot = new Robot();
	} catch(Exception e){
	    fail("Could not get a Robot instance to drive the test case: " + e);
	}
	robot.mouseMove(linkPos.x+22, linkPos.y-10); // somewhere in the window to get focus
	sleep(1000);
	robot.mouseMove(linkPos.x+2, linkPos.y+10);
	sleep(2000);
	robot.mousePress(InputEvent.BUTTON1_MASK);
	// drag (mouse is pressed)
	sleep(1000);
	robot.mouseMove(linkPos.x+3, linkPos.y+10);
	sleep(1000);
	robot.mouseMove(linkPos.x+4, linkPos.y+10);
	sleep(1000);
	robot.mouseMove(dropPos.x+10, dropPos.y+3);
	sleep(1000);
	robot.mouseMove(dropPos.x+11, dropPos.y+3);
	sleep(2000);
	// drop
	robot.mouseRelease(InputEvent.BUTTON1_MASK);	
	sleep(2000);

	// see that the dropped value is an expected
	assertTrue("The dropped text (" + dropField.getText() + ") is not the same as " +
		   "the dragged value (" + TEST_AA_SEQ + ")", TEST_AA_SEQ.equals(dropField.getText()));

	frame.dispose(); //needed, otherwise the JVM might hang indeifinitely after the tests
    }

    private Component getSubComponentByClass(Container container, Class clazz){
	if(clazz == null){
	    return null;
	}

	Component[] subcomponents = container.getComponents();
	for(int i = 0; i < subcomponents.length; i++){
	    if(subcomponents[i] != null && clazz.isInstance(subcomponents[i])){
		return subcomponents[i];
	    }
	    if(subcomponents[i] instanceof Container){
		Component subAnswer = getSubComponentByClass((Container) subcomponents[i], clazz);
		if(subAnswer != null){
		    return subAnswer;
		}
	    }
	}
	return null;
    }

    private Component getSubComponentByName(Container container, String subName){
	if(subName == null){
	    return null;
	}

	Component[] subcomponents = container.getComponents();
	for(int i = 0; i < subcomponents.length; i++){
	    String name = subcomponents[i].getName();
	    if(subName.equals(name)){
		return subcomponents[i];
	    }
	    if(subcomponents[i] instanceof Container){
		Component subAnswer = getSubComponentByName((Container) subcomponents[i], subName);
		if(subAnswer != null){
		    return subAnswer;
		}
	    }
	}
	return null;
    }

    // Test if hitting a regular HTML hyperlink launches an external browser
    // This seems to actually be pretty hard to do, so I'll put this one off.
    // We should really use JDIC when it becomes a stable release
    public void testExternalLinks(){
	
    }

    // TO DO, test tab management
    public void testCloseTab(){
    }

    public void testCloseOtherTabs(){
    }

    /** loads the given data and provides the location on screen of its hyperlink*/
    private Point loadSeqAndLink(String sequenceData, String textToGet) throws Exception{
	// load a dna sequence in a tab
	MobyDataObject mobyObject = MobyUtils.createMobySequence(sequenceData, "foo");
	contentGUI.loadPaneFromObject(new MobyContentInstance(mobyObject, "hyperlink-test"), true);  //true = load in new tab

	// give the JVM time to paint the new data
	sleep(2000);

	// left-click the link
	JEditorPane pane = contentGUI.getCurrentPane().getDisplay();
	Point screenPos = pane.getLocationOnScreen();
	// The following value is dependent on the stylesheet used to make the HTML display
	String text = pane.getDocument().getText(0, pane.getDocument().getLength());
	// Find the position of the first XPointer link (should be a reference 
	// to the file version of the DNASequence we created above)
	String dataType = mobyObject.getDataType().getName();
	if(textToGet == null){
	    textToGet = dataType;
	}
	int hyperlinkModelIndex = text.indexOf(textToGet);
	assertFalse("Could not find a MOBY link for \"" + textToGet + 
		    "\" in the "+dataType+" representation", hyperlinkModelIndex == -1);

	try{
	    Rectangle linkPos = pane.modelToView(hyperlinkModelIndex+4);
	    // Assumes that the link is already on the screen.  Otherwise, you will need to add a bunch
	    // of code here to compensate for the scroll pane the text display is inside...
	    screenPos.x += linkPos.x;
	    screenPos.y += linkPos.y;
	}
	catch(javax.swing.text.BadLocationException ble){
	    System.err.println(text);
	    fail("The pattern to find a hyperlink in the document doesn't find a visible part of the HTML " +
		 "(retooling of test required), index found was " + hyperlinkModelIndex);
	}

	return screenPos;
    }

    // returns the text label of the service menu item invoked
    public String findService(String sequenceData, String targetMenuText, String targetServiceLabelText) throws Exception{
	Point screenPos = loadSeqAndLink(sequenceData, null);

	Robot robot = new Robot();
	robot.mouseMove(screenPos.x, screenPos.y+10);
	sleep(2000);
	robot.mouseMove(screenPos.x-8, screenPos.y+10);
	sleep(2000);
	//robot.mouseMove(screenPos.x+10, screenPos.y+10);
	//sleep(2000);
	robot.mousePress(InputEvent.BUTTON1_MASK);
	robot.mouseRelease(InputEvent.BUTTON1_MASK);
	sleep(2000);
	finder.setName(MobyContentPane.MOBY_SERVICE_POPUP_NAME);
	Object openPopup = finder.find();
	assertNotNull("Clicking the hyperlink did not open the service options popup (was null)", 
		      openPopup);
	assertTrue("Component launched by clicking hyperlink was not a JPopupMenu as expected (was a " + 
		   openPopup.getClass().getName(),
		   openPopup instanceof JPopupMenu);

	// Move the mouse a bit to the right to bring up the submenu
	robot.mouseMove(screenPos.x+10, screenPos.y+10);

	// Find a submenu with services
	Component serviceTypeChoices = getSubComponentByName((JPopupMenu) openPopup, 
							MobyServicesGUI.SERVICE_SUBMENU_NAME);
	assertNotNull("The service options popup (spawned by a hyperlink) did not create any service submenus", 
		      serviceTypeChoices);
	assertTrue("Component launched by clicking hyperlink was not a JMenu as expected (was a " + 
		   serviceTypeChoices.getClass().getName(),
		   serviceTypeChoices instanceof JMenu);
	while(((JMenu) serviceTypeChoices).getItemCount() == 2){
	    sleep(1000);  //wait for the service options to populate the menu...
	}
	sleep(500);

	JMenuItem serviceTypeChosen = ((JMenu) serviceTypeChoices).getItem(1);
	for(int i = 2;
	    serviceTypeChosen.getText().indexOf(targetMenuText) == -1 && 
		i <= ((JMenu) serviceTypeChoices).getItemCount(); 
	    i++){
	    if(i == ((JMenu) serviceTypeChoices).getItemCount()){
		serviceTypeChosen = null; break;
	    }
	    else{
		serviceTypeChosen = ((JMenu) serviceTypeChoices).getItem(i);
	    }
	}
	assertNotNull("Could not find Analysis services submenu required to test service execution",
		      serviceTypeChosen);
 	Point submenuPos = serviceTypeChosen.getLocationOnScreen();
 	// Put mouse on parsing service item to show submenu...
 	robot.mouseMove(submenuPos.x, submenuPos.y);
 	sleep(1000);
 	serviceTypeChoices = serviceTypeChosen;

 	Vector<JMenuItem> servicePathChosen = findMenuItem((JMenu) serviceTypeChosen, targetServiceLabelText);
	if(servicePathChosen == null || servicePathChosen.size() == 0){
	    throw new Exception("Cannot find a target service, "+
			       "none were found ending in '"+targetServiceLabelText+"'");
	}
	// Make sure it's visible
	showMenuItem((JMenu) serviceTypeChosen, servicePathChosen, robot);
	Point serviceMenuItemLoc = servicePathChosen.lastElement().getLocationOnScreen(); //so move the mouse there too
	// and click the service button
	robot.mouseMove(serviceMenuItemLoc.x+2, serviceMenuItemLoc.y+2);
	robot.keyPress(KeyEvent.VK_SHIFT);
	robot.mousePress(InputEvent.BUTTON1_MASK);  
	robot.mouseRelease(InputEvent.BUTTON1_MASK);  
	robot.keyRelease(KeyEvent.VK_SHIFT);
	//serviceChosen.doClick(300);
	sleep(2000);

	return servicePathChosen.elementAt(servicePathChosen.size()-1).getText();
    }

    public void testRunAsynchronousService() throws Exception{
	String serviceLabel = null;
	try{
	    serviceLabel = findService(TEST_AA_SEQ, "ObjectHandling", "(async)");
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Error while finding a service matching \"(async)\" " +
		 "(asynchronous execution) under \"ObjectHandling\" in the popup");
	}

	if(serviceLabel.indexOf("...") != -1){
	    fillInSecondariesAndExecute();
	}

	// Wait until the job is finished to proceed to the next test
	while(contentGUI.getCurrentURL() == null){
	    sleep(1000);
	    if(contentGUI.isShowingFailure()){
		break;
	    }
	}
	sleep(2000); //just to ensure HTML rendering finishes		
    }

    public void testRunServiceSecondariesAndWorkflow() throws Exception{
	try{
	    findService(TEST_DNA_SEQ, "BasicGFFSequenceFeature", "...");
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Error while a finding service ending with \"...\" " +
		 "(secondary input) under \"BasicGFFSequenceFeature\" in the popup");
	}
	fillInSecondariesAndExecute();

	// Wait until the job is finished to proceed to the next test
	while(contentGUI.getCurrentURL() == null){
	    sleep(1000);
	    if(contentGUI.isShowingFailure()){
		break;
	    }
	}
	sleep(2000); //just to ensure HTML rendering finishes	

	// Since we ran a real service, we can make a Taverna workflow out of the tab history
	File testFile = File.createTempFile("test-seahawk", "");
	testFile.deleteOnExit();
	MobySaveDialog.exportWorkflow(contentGUI.getCurrentPane(), testFile);
	sleep(1000);
	assertTrue("The workflow saved data file was empty", testFile.length() != 0);
    }

    public void fillInSecondariesAndExecute() throws Exception{
	Robot robot = new Robot();
	JDialog secondaryParamsDialog = null;
	
	sleep(10000);  // wait for dialog to popup, may take a while when meta-data about the service is fetched
	DialogFinder dfinder = new DialogFinder(".*");
	List showingDialogs = dfinder.findAll();
	for(int i = 0; i < showingDialogs.size(); i++){
	    JDialog dialog = (JDialog) showingDialogs.get(i);
	    if(MobySecondaryInputGUI.TITLE.equals(dialog.getTitle())){
		secondaryParamsDialog = dialog;
		break;
	    }
	}

	assertNotNull("Requesting a service with secondary parameters did not launch " +
		      "a secondary parameter input component (was null)", 
		      secondaryParamsDialog);
	assertTrue("Secondary parameter input component was not a JDialog as expected (was a " + 
		   secondaryParamsDialog.getClass().getName(),
		   secondaryParamsDialog instanceof JDialog);
	Component okButton = getSubComponentByName(secondaryParamsDialog.getContentPane(), 
						   MobySecondaryInputGUI.OK_BUTTON_NAME);
	assertNotNull("Could not find the OK button in the secondary input GUI dialog", okButton);
	Point okLoc = okButton.getLocationOnScreen(); //so move the mouse there too
	// and click the OK button
	robot.mouseMove(okLoc.x+2, okLoc.y+2);
	robot.mousePress(InputEvent.BUTTON1_MASK);  
	robot.mouseRelease(InputEvent.BUTTON1_MASK);  
	sleep(2000);

	showingDialogs = dfinder.findAll();
	for(int i = 0; i < showingDialogs.size(); i++){
	    JDialog dialog = (JDialog) showingDialogs.get(i);
	    if(dialog.isVisible()){
		assertFalse("The secondary parameter window did not disappear when enter was pressed (" +
			    "either the approval button does not have the default focus, or " +
			    "there is an internal problem with the dialog validation)", 
			    MobySecondaryInputGUI.TITLE.equals(dialog.getTitle()));
	    }
	}
	
    }

    // Load a document, then highlight a salient section that can be turned into Web Service choices
    // via MobyServicesGUI.
    public void testHighlightOptions() throws Exception{

	// load a dna sequence in a tab
	MobyDataObject mobyObject = MobyUtils.createMobySequence(TEST_DNA_SEQ, "foo");
	contentGUI.loadPaneFromObject(new MobyContentInstance(mobyObject, "highlight-test"), true);  //true = load in new tab

	// now highlight (drag across) the text right-to-left to generate a reverse
	// complemented dna sequence to submit to services
	// left-click the link
	sleep(1000);
	JEditorPane pane = contentGUI.getCurrentPane().getDisplay();
	Point screenStartPos = pane.getLocationOnScreen();
	Point screenEndPos = pane.getLocationOnScreen();
	// The following value is dependent on the stylesheet used to make the HTML display
	String text = pane.getDocument().getText(0, pane.getDocument().getLength());
	// Find the position of the first XPointer link (should be a reference 
	// to the file version of the DNASequence we created above)
	int dnaSeqModelIndex = text.indexOf(TEST_DNA_SEQ);
	assertFalse("Could not find the actual sequence in the DNASequence representation", dnaSeqModelIndex == -1);

	try{
	    // Hightlight backward (3' to 5') so that the reverse complement facility is used

	    Rectangle startPos = pane.modelToView(dnaSeqModelIndex+TEST_DNA_SEQ.length());
	    // Assumes that the link is already on the screen.  Otherwise, you will need to add a bunch
	    // of code here to compensate for the scroll pane the text display is inside...
	    screenStartPos.x += startPos.x;
	    screenStartPos.y += startPos.y;

	    Rectangle endPos = pane.modelToView(dnaSeqModelIndex);
	    // Assumes that the link is already on the screen.  Otherwise, you will need to add a bunch
	    // of code here to compensate for the scroll pane the text display is inside...
	    screenEndPos.x += endPos.x;
	    screenEndPos.y += endPos.y;
	}
	catch(javax.swing.text.BadLocationException ble){
	    System.err.println(text);
	    fail("The pattern to find the sequence start in the document doesn't find a visible " +
		 "part of the HTML (retooling of test required), index found was " + dnaSeqModelIndex);
	}

	// Drag the mouse across the range found for the sequence 3' to 5', then click the highlighted region
	Robot robot = new Robot();
	robot.mouseMove(screenStartPos.x, screenStartPos.y);
	robot.mousePress(InputEvent.BUTTON1_MASK);
	sleep(400);
	robot.mouseMove(screenEndPos.x, screenEndPos.y);
	robot.mouseRelease(InputEvent.BUTTON1_MASK);
	sleep(1000);
	robot.mouseMove(screenEndPos.x+2, screenEndPos.y+2);
	robot.mousePress(InputEvent.BUTTON1_MASK);
	robot.mouseRelease(InputEvent.BUTTON1_MASK);
	sleep(3000);
	finder.setName(MobyContentPane.MOBY_SERVICE_POPUP_NAME);
	Object servicePopup = finder.find();
	assertNotNull("Clicking the highlighted region did not open the service options popup (was null)", 
		      servicePopup);
	assertTrue("Component launched by clicking highighlighted region was not a JPopupMenu as expected (was a " + 
		   servicePopup.getClass().getName(),
		   servicePopup instanceof JPopupMenu);

	// Move the mouse a bit to the right to bring up the submenu
	robot.mouseMove(screenEndPos.x+10, screenEndPos.y);
	sleep(1000);

	// Should include some fancy check here that the rev-comp was actually done ... TO DO
	Component serviceTypeChoices = getSubComponentByName((JPopupMenu) servicePopup, 
							 MobyServicesGUI.SERVICE_SUBMENU_NAME);
	assertNotNull("The service options popup (spawned by a highlight selection) did not create any service submenus", 
		      serviceTypeChoices);
	assertTrue("Component launched by clicking highlighted region was not a JMenu as expected (was a " + 
		   serviceTypeChoices.getClass().getName(),
		   serviceTypeChoices instanceof JMenu);
	
	// There should be at least two top level menus, one for DNASequence, the other for String
	assertTrue("The services popup did not have at least two submenus (should have " +
		   "DNASequence and String options)",
		   ((JPopupMenu) servicePopup).getSubElements() != null && 
		   ((JPopupMenu) servicePopup).getSubElements().length > 1);

	while(((JMenu) serviceTypeChoices).getItemCount() == 2){
	    sleep(1000);  //wait for the service options to populate the menu...
	}
	sleep(500);  //give time to draw the menu again

 	Vector<JMenuItem> servicePathChosen = findMenuItem((JMenu) serviceTypeChoices, "ExplodeOutCrossReferences");
	if(servicePathChosen == null || servicePathChosen.size() == 0){
	    System.err.println("WARNING: Skipping test of service run, no 'ExplodeOutCrossReferences' service was found");
	    return;
	}
	// Make sure it's visible
	showMenuItem((JMenu) serviceTypeChoices, servicePathChosen, robot);

	servicePathChosen.lastElement().doClick(300);
	sleep(10000);
	DialogFinder dfinder = new DialogFinder(".*");
	List showingDialogs = dfinder.findAll();
	for(int i = 0; i < showingDialogs.size(); i++){
	    JDialog dialog = (JDialog) showingDialogs.get(i);
	    assertFalse("Dialogs with title \""+MobySecondaryInputGUI.TITLE+
			"\" was found, but service launched had no secondary params", 
			MobySecondaryInputGUI.TITLE.equals(dialog.getTitle()));
	}

	// Wait until the job is finished to proceed to the next test
	while(contentGUI.getCurrentURL() == null){
	    sleep(1000);
	    if(contentGUI.isShowingFailure()){
		break;
	    }
	}
	sleep(2000); //just to ensure HTML rendering finishes
    }
    
    public void testExternalBrowser() throws Exception{
	assertNotNull("Could not find an external browser", ca.ucalgary.seahawk.util.BrowserLauncher.locateBrowser());
    }

    public void testMain() throws Exception{
	// Clear the cache to ensure cache loading classes get used
	Central central = CentralImpl.getDefaultCentral(null);
	if(central instanceof CentralCached){
	    ((CentralCached) central).removeFromCache(null);
	}
	MobyContentGUI.setDefaultAppCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	MobyContentGUI.main(new String[]{});
    }

    public void testUserPreferences() throws Exception{
	File tempFile = File.createTempFile("test-seahawk", "");
	try{
	    java.io.FileOutputStream tempFileOS = new java.io.FileOutputStream(tempFile);
	    tempFileOS.write("junk".getBytes());
	    tempFileOS.close();
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not write to temporary file " + tempFile + ": " + e);
	}
	boolean failedAsShould = false;
	try{
	    SeahawkOptions.restoreSettings(tempFile.toURI().toURL());
	} catch(Exception e){
	    failedAsShould = true;
	}
	assertTrue("No exception was thrown, despite a poorly formatted preference file", 
		   failedAsShould);

	SeahawkOptions.setCacheExpiry(10.0);
	assertTrue("Setting the cache expiry preference did not work, expected " +
		   "current value of 10, but found " + SeahawkOptions.getCacheExpiry(),
		   SeahawkOptions.getCacheExpiry() == 10.0);
	try{
	    java.io.FileOutputStream tempFileOS = new java.io.FileOutputStream(tempFile);
	    SeahawkOptions.saveSettings(tempFileOS);
	    tempFileOS.close();
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not save Seahawk settings to file " + tempFile + ": " + e);
	}
	SeahawkOptions.setCacheExpiry(20.0);
	try{
	    SeahawkOptions.restoreSettings(tempFile.toURI().toURL());
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not restore Seahawk settings from file " + tempFile + ": " + e);
	}
	assertTrue("The settings were not saved/restored properly, " +
		   "expected cache expiry of 10, but got 20",
		   SeahawkOptions.getCacheExpiry() == 10.0);
	tempFile.delete();
    }

    // This is done interactively, not automatically
    public void testDaggoo() throws Exception{

    }

    public void testWordFileConversion() throws Exception{
	
    }

    public void testExcelFileConversion() throws Exception{
	
    }

    public void testTeXFileConversion() throws Exception{
	
    }

    /**
     * @return a test suite for all the test methods of this test case.
     */
    public static Test suite() {

	TestSuite suite = new TestSuite();
  	suite.addTest(new SeahawkTestCase("testMain")); //done
  	suite.addTest(new SeahawkTestCase("testHelpTab")); //done
  	suite.addTest(new SeahawkTestCase("testHelpButton")); //done
  	suite.addTest(new SeahawkTestCase("testClipboardTab")); //done
  	suite.addTest(new SeahawkTestCase("testOpenButton"));  //done
	suite.addTest(new SeahawkTestCase("testSave"));  //done
  	suite.addTest(new SeahawkTestCase("testPrint"));  //done
  	suite.addTest(new SeahawkTestCase("testNavigationButtons")); //done
  	suite.addTest(new SeahawkTestCase("testRunAsynchronousService")); //done
  	suite.addTest(new SeahawkTestCase("testRunServiceSecondariesAndWorkflow")); //done
  	suite.addTest(new SeahawkTestCase("testHighlightOptions"));//done
  	suite.addTest(new SeahawkTestCase("testUserPreferences"));//done
  	suite.addTest(new SeahawkTestCase("testExternalBrowser"));//done
  	suite.addTest(new SeahawkTestCase("testWSDL")); // done
	suite.addTest(new SeahawkTestCase("testDragnDrop")); //done
	suite.addTest(new SeahawkTestCase("testDaggoo"));
//   	suite.addTest(new SeahawkTestCase("testWordFileConversion"));
//   	suite.addTest(new SeahawkTestCase("testExcelFileConversion"));
//   	suite.addTest(new SeahawkTestCase("testTeXFileConversion"));
        return suite;
    }

    /**
     * Runs the test suite when the class is invoked on the command line.
     * The name of the service can be defined through a Java property, 
     * moby.testService
     */
    public static void main(String args[]) throws Exception{
	
        junit.textui.TestRunner.run(suite());
    } 

    public void showMenuItem(JMenu menu, Vector<JMenuItem> path, Robot robot) throws Exception{
	for(int i = 0; i < path.size(); i++){
	    JMenuItem pathItem = path.elementAt(i);
	    for(int j = 0; j < menu.getItemCount(); j++){
		if(pathItem.equals(menu.getItem(j))){
		    Point serviceMenuItemLoc = pathItem.getLocationOnScreen(); //so move the mouse there too
		    // and click the service button
		    robot.mouseMove(serviceMenuItemLoc.x+2, serviceMenuItemLoc.y+2);
		    if(pathItem instanceof JMenu){  //not terminal
			menu = (JMenu) pathItem;
		    }
		    else{
			i = path.size();  //terminal, end outer loop
		    }
		    Thread.sleep(1000);
		    break;
		}
	    }
	}
    }

    // Depth-first search of submenus for a menu item matching the pattern 
    public Vector<JMenuItem> findMenuItem(JMenu menu, String pattern){
	Vector<JMenuItem> result = new Vector<JMenuItem>();
	findMenuItem(menu, pattern, result);
	return result;
    }

    public boolean findMenuItem(JMenu menu, String pattern, Vector<JMenuItem> result){
	JMenuItem serviceChosen = null;
	for(int i = 0;
	    i <= menu.getItemCount(); 
	    i++){
	    if(i == menu.getItemCount()){
		return false;
	    }

	    serviceChosen = menu.getItem(i);
	    if(serviceChosen.getText().indexOf(pattern) != -1){
		break;
	    }
	    else{
		// Nested menu
		if(serviceChosen instanceof JMenu){
		    if(findMenuItem((JMenu) serviceChosen, pattern, result)){
			break;
		    }
		}
	    }
	}
	result.insertElementAt(serviceChosen, 0); //build the menu tree path
	//System.err.println("Returning menu element " + serviceChosen);
	return true;
    }
}
