package org.greenstone.gsdl3.testing; import java.io.File; import java.util.Map; import java.util.regex.*; import java.awt.Component; // only JUnit and JAssert-Swing, no Selenium in this class import org.junit.Assert; import org.assertj.swing.fixture.*; import org.assertj.swing.timing.Timeout ; // GLI imports import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.GathererProg; // main GLI class we'll be testing import org.greenstone.gatherer.Dictionary; // access to display strings // Java GUI testing with AssertJ Swing import org.assertj.swing.junit.testcase.AssertJSwingJUnitTestCase; import org.assertj.swing.fixture.*; import org.assertj.swing.edt.GuiActionRunner; import org.assertj.swing.core.Robot; // static imports import static org.assertj.swing.launcher.ApplicationLauncher.*; import static org.assertj.swing.finder.WindowFinder.findFrame; /* * Utility class containing helper functions for GLI GUI testing using AsssertJ Swing */ public class GSGUITestingUtil { public static FrameFixture window = null; public static final boolean PAUSE_ON = true; public static final int SECOND = 1000; // 1000 ms public static final String DOWNLOAD_PANE = "Download"; public static final String GATHER_PANE = "Gather"; public static final String ENRICH_PANE = "Enrich"; public static final String DESIGN_PANE = "Design"; public static final String CREATE_PANE = "Create"; public static final String FORMAT_PANE = "Format"; /************** NEEDED FOR TESTING *************/ // There's now a new method in GLI: GUIManager.setNamesRecursively() // that attempts to recursively call setName() on visible GUI components so that we // can have easier access to those GUI components when testing here through their names /****************************** APPLICATION *******************************/ public static void PAUSE() { PAUSE(5); } public static void PAUSE(int seconds) { if(!PAUSE_ON) return; // ignore calls to PAUSE // wait a couple of seconds again? try{ Thread.sleep(seconds*SECOND); } catch(Exception e) { e.printStackTrace(); } } //public static void setWindow(FrameFixture w) { window = w; } // if we want to null window public static void runGLI() { //GathererProg frame = GuiActionRunner.execute(() -> new GathererProg()); // IMPORTANT, note the call to 'robot()': must use the Robot from AssertJSwingJUnitTestCase //window = new FrameFixture(robot(), frame); //window.show(); // shows the frame to test // Instead, we'll be launching GLI and then getting a ref to the launched app window: // Launch GathererProg.java's main() method // See https://joel-costigliola.github.io/assertj/assertj-swing-launch.html String GSDLOS = System.getenv("GSDLOS"); String GSDLHOME = System.getenv("GSDLHOME"); String GSDL3HOME = System.getenv("GSDL3HOME"); String GSDL3SRCHOME = System.getenv("GSDL3SRCHOME"); application("org.greenstone.gatherer.GathererProg").withArgs( "-gsdl", GSDLHOME, "-gsdlos", GSDLOS, "-gsdl3", GSDL3HOME, "-gsdl3src", GSDL3SRCHOME, "-testing_mode").start(); // The -testing_mode flag passed into GLI above is a special flag // that calls GLI's TestingPreparating.setNamesRecursively() // to help testing by calling setName() on GUI components. } public static void exitGLI() { openMenuItem("file", "exit"); } // Gets a ref to the main window (FrameFixture) of a running GLI public static FrameFixture getGLIApplicationWindow(Robot robot) { // locate and set the internal FrameFixture member variable 'window' and return a handle to it window = findFrame("GUIManager").using(robot); // GLI application JFrame's name is set to its classname, 'GUIManager' // https://joel-costigliola.github.io/assertj/swing/api/index.html?org/assertj/swing/launcher/ApplicationLauncher.html // "To change the speed of a GUI test, you need to change the values of both delayBetweenEvents and eventPostingDelay." //robot.settings().delayBetweenEvents(50); //robot.settings().eventPostingDelay(2*SECOND); // annoying, after every click! return window; } /****************************** BASICS *******************************/ //https://joel-costigliola.github.io/assertj/swing/api/org/assertj/swing/fixture/FrameFixture.html public static void switchToPane(String pane) { String paneLabel = Dictionary.get("GUI." + pane); // e.g. GUI.Enrich //JPanelFixture tab = window.panel(pane); // this just gets us the JPanel of controls within JTabbedPaneFixture tab = window.tabbedPane("GUIManager.tab_pane"); tab.selectTab(paneLabel); // select tab by its title } public static void openMenuItem(String menu, String subMenu) { /*String menuLabel = Dictionary.get("Menu." + menu); // e.g. Menu.File window.menuItem(menuLabel).click(); //JMenuItemFixture.click() String subMenuLabel = Dictionary.get(menuLabel + "_" + subMenu); // e.g. Menu.File_Open window.menuItem(subMenuLabel).click();*/ window.menuItem("MenuBar."+menu).click(); window.menuItem("MenuBar."+menu+"_"+subMenu).click(); } /* public static void stealAnyLock() { DialogFixture dialog = window.dialog("LockFileDialog"); if(dialog != null) { dialog.button("LockFileDialog.ok_button").click(); } }*/ // e.g. pane = Enrich,view=collection; pane = Gather, view = workspace (or collection) public static void getFolderPath(String pane, String view, String folderPath) {} public static void getFilePath(String pane, String view, String filePath) {} /**********************FILE MENU*******************************/ public static void setPrefs(String tab, Map params) { openMenuItem("file", "options"); } /** * @param toMode must be the dictionary key for Preferences mode. * Choose from Preferences.Mode.Assistant, Preferences.Mode.Librarian, Preferences.Mode.Expert */ public static void changeUserMode(String toMode) { openMenuItem("file", "options"); // click MenuBar.file -> MenuBar.file_options // switch to Mode tab String paneLabel = Dictionary.get("Preferences.Mode"); DialogFixture dialog = window.dialog("Preferences"); JTabbedPaneFixture tab = window.tabbedPane("Preferences.tab_pane"); tab.selectTab(paneLabel); // select tab by its title String mode = "assistant"; // library assistant if(toMode.contains("xpert")) { mode = "expert"; } else if (toMode.endsWith("ibrarian")) { mode = "librarian"; } dialog.radioButton("Preferences."+mode+"_mode_radio_button").check(true); // apply and close dialog dialog.button("Preferences.apply_button").click(); dialog.button("Preferences.ok_button").click(); } public static void closeCollection(){ // Through componennt names, clicks on MenuBar.file then MenuBar.file_close openMenuItem("file", "close"); } public static void deleteCollection(String collName) { openMenuItem("file", "delete"); DialogFixture dialog = window.dialog("DeleteCollectionPrompt"); // could call window.dialg() too as there will be only one, unless GLI run in debug mode? JListFixture collection_list = dialog.list("DeleteCollectionPrompt.list"); Pattern collNameRegex = Pattern.compile(".*"+collName+".*"); collection_list.selectItem(collNameRegex); collection_list.requireSelection(collNameRegex); // assert it exists and is selectable. Can't select (or delete) coll if it's open dialog.checkBox("DeleteCollectionPrompt.confirmation").check(true); // check checkbox to be sure to delete dialog.button("DeleteCollectionPrompt.ok_button").click(); // delete button // A close confirmation optionPane dialog appears - click OK in it dialog.optionPane().okButton().click(); dialog.button("DeleteCollectionPrompt.close_button").click(); // close deleteColl dialog } public static void saveCollection() { openMenuItem("file", "save"); // MenuBar.file -> MenuBar.file_save } public static void createCollection(String collTitle, String collDescription, String baseColl) { openMenuItem("file", "new"); DialogFixture dialog = window.dialog("col"); // GLI is unable to setName() of this dialog // to NewCollectioNDetailsPrompt. Not sure why. dialog.textBox("NewCollectionDetailsPrompt.title").enterText(collTitle); // JTextField if(collDescription != null && !collDescription.equals("")) { // JTextArea description dialog.textBox("NewCollectionDetailsPrompt.description").enterText(collDescription); } // JComboBox base_collection if(baseColl != null && !baseColl.equals("")) { JComboBoxFixture baseCollBox = dialog.comboBox("NewCollectionDetailsPrompt.base_collection"); Pattern collNameRegex = Pattern.compile(".*"+baseColl+".*"); // comboBox should CONTAIN this string baseCollBox.selectItem(collNameRegex); baseCollBox.requireSelection(collNameRegex); // assert the baseColl name exists } dialog.button("NewCollectionDetailsPrompt.create_button").click(); } public static void loadCollection(String collName) { openMenuItem("file", "open"); DialogFixture dialog = window.dialog("OpenCollectionDialog"); JListFixture collection_list = dialog.list("OpenCollectionDialog.collection_list"); // See section 4.2 of http://www.vogella.com/tutorials/JavaRegularExpressions/article.html // Apparently, testing Pattern "collName" expects it to match in entirety // whereas testing ".*collName.*" will test the string contains this Pattern Pattern collNameRegex = Pattern.compile(".*"+collName+".*"); /*String[] items = collection_list.contents(); for(String item : items) { System.err.println("@@@ ITEM: " + item); }*/ //JListItemFixture collItem = collection_list.item(collNameRegex); //collItem.click(); //collection_list.requireSelection(collNameRegex); // assert that the requested collection exists //collItem.doubleClick(); collection_list.selectItem(collNameRegex); collection_list.requireSelection(collNameRegex); // assert that the requested collection exists /*String[] selections = collection_list.selection(); for(String s : selections) { System.err.println("@@@ opening collection " + s); }*/ JListItemFixture selectedColl = collection_list.item(collNameRegex); //collection_list.doubleClick(); // will double-click on last item regardless of what's selected selectedColl.doubleClick(); //steal any lock /* DialogFixture dialog = window.dialog("LockFileDialog"); if(dialog != null) { dialog.button("LockFileDialog.ok_button").click(); } */ } /** * @param exportColl has to be internal GS collection name, e.g. lucene-jdbm-demo * not public collection name like "Demo Collection". */ public static void exportCollection(String exportToFormat, String exportColl) { openMenuItem("file", "exportas"); // Clicks MenuBar.file -> MenuBar.file_exportas DialogFixture dialog = window.dialog("ExportAsPrompt"); dialog.comboBox("ExportAsPrompt.saveas_combobox").selectItem(exportToFormat); Pattern collNameRegex = Pattern.compile(".*"+exportColl+".*"); JListFixture collection_list = dialog.list("ExportAsPrompt.list"); collection_list.selectItem(collNameRegex); //JList of collections collection_list.requireSelection(collNameRegex); // check it exists dialog.button("ExportAsPrompt.ok_button").click(); // do export // first we see a progress dialog // Wait for subsequent export complete dialog DialogFixture exportCompleteDialog = window.dialog("SimpleResultDialog", Timeout.timeout(30*SECOND)); // non-modal dialog, so give it focus, so we can close it through its Close button exportCompleteDialog.focus(); //exportCompleteDialog.button().click(); exportCompleteDialog.button("SimpleResultDialog.GLIButton."+Dictionary.get("General.Close")).click(); // it only has a single button: "Close", still want to future proof if more buttons get added by specifying the particular button // Close outer dialog too - cancel doubles as close button now that export is complete dialog.button("ExportAsPrompt.cancel_button").click(); File file = new File(System.getenv("GSDLHOME") + File.separator + "tmp" + File.separator + "exported_"+exportColl+"_"+exportToFormat); Assert.assertTrue(file.exists()); Assert.assertTrue(file.isDirectory()); } /*************** DESIGN ******************************/ public static void changeIndexer(String toIndexer) {} public static void changeDB(String toDB) {} public static void configurePartitionIndex(String tab, Map params) {} public static void configurePlugin(String pluginName, Map params) { } public static void configureClassifier(String classifierName, Map params) {} public static void configurePlugout(String plugoutName, Map params) {} // https://www.journaldev.com/1257/java-varargs // https://www.geeksforgeeks.org/variable-arguments-varargs-in-java/ public static void keepOnlyPlugins(String ... plug_n) {} public static void removePlugs(String ... plug_n) {} /********************** CREATE PANEL ********************/ public static void buildOpenCollection() {} public static void buildOpenCollection(boolean minimalRebuild) {} public static void configureImportOptions(Map params) {} public static void configureBuildOptions(Map params) {} public static void buildOutputContains(String ... n) {} /******************** GATHER PANE ********************/ public static void createWorkspaceShortcut(){} // in current open collection public static void createCollectionSubfolder() {} public static void dragNDrop(String workspacePath, String collPath) {} /********************* ENRICH PANE ******************/ // docPath can be just docName or collectionSubfolder/Subfolder2/docName public static void addMeta(String docPath, Map metanamesToValues) {} public static void addFolderLevelMeta(String folderPath, Map metanamesToValues) {} /********************* DOWNLOAD PANE ******************/ // TODO: more functions needed here: e.g. serverInfo? public static void download(String downloader, Map params) {} public static void clearCache() {} public static String serverInfo() { return ""; } /********************* DOWNLOAD PANE ******************/ public static void formatGeneral(){} public static void formatSearch() {} public static void formatFeature(String featureName, String filename) {} public static boolean isFormatFeatureXMLValid() { return true; } /********************* GEMS ******************/ // TODO: public static void runGEMS() {} public static void exitGEMS() {} public static FrameFixture getGEMSApplicationWindow(Robot robot) { // TODO: don't forget to set internal member variable called 'window' before returning it return window; } // Moving these working bits of code here, in case I can use them to right // general get methods that make use of the GenericTypeMatcher method of accessing components // See https://joel-costigliola.github.io/assertj/assertj-swing-lookup.html /*window = findFrame(new GenericTypeMatcher(JFrame.class) { protected boolean isMatching(JFrame window) { return window.getTitle().trim().startsWith(expectedWindowTitle) && window.isShowing(); } }).using(robot()); */ /* JTabbedPaneFixture tab = window.tabbedPane(new GenericTypeMatcher(JTabbedPane.class) { @Override protected boolean isMatching(JTabbedPane tabPane) { System.err.println("### trying for match"); //int index = GuiActionRunner.execute(() -> tabPane.getSelectedIndex()); int index = tabPane.getSelectedIndex(); String selectedTabTitle = tabPane.getTitleAt(index); //GuiActionRunner.execute(() -> tabPane.getTitleAt(index)); System.err.println("### GOT TITLE: " + selectedTabTitle); return gatherPaneLabel.equals(selectedTabTitle); } }); */ }