1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * 
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  * 
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  * 
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  * 
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  * 
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.test.jemmy.misc.wrappers;
  34 
  35 import java.util.ArrayList;
  36 import java.util.List;
  37 
  38 import org.eclipse.jface.dialogs.IDialogConstants;
  39 import org.eclipse.swt.graphics.Image;
  40 import org.eclipse.swt.widgets.Button;
  41 import org.eclipse.swt.widgets.Display;
  42 import org.eclipse.swt.widgets.Shell;
  43 import org.jemmy.Point;
  44 import org.jemmy.control.Wrap;
  45 import org.jemmy.interfaces.Parent;
  46 import org.jemmy.lookup.Lookup;
  47 import org.jemmy.swt.ControlWrap;
  48 import org.jemmy.swt.lookup.ByName;
  49 import org.jemmy.swt.lookup.ByTextControlLookup;
  50 import org.junit.Assert;
  51 
  52 import org.openjdk.jmc.test.jemmy.misc.base.wrappers.MCJemmyBase;
  53 import org.openjdk.jmc.test.jemmy.misc.fetchers.Fetcher;
  54 
  55 /**
  56  * The Jemmy wrapper for Buttons
  57  */
  58 public class MCButton extends MCJemmyBase {
  59 
  60         private MCButton(Wrap<? extends Button> button) {
  61                 this.control = button;
  62         }
  63 
  64         /**
  65          * Finds a button in the supplied shell by image and returns it.
  66          *
  67          * @param shell
  68          *            the shell where to search for the button
  69          * @param image
  70          *            the image to look up the button with
  71          * @return a {@link MCButton} (possibly null)
  72          */
  73         @SuppressWarnings("unchecked")
  74         public static MCButton getByImage(Wrap<? extends Shell> shell, Image image) {
  75                 List<Wrap<? extends Button>> allVisibleButtonWraps = getVisible(
  76                                 shell.as(Parent.class, Button.class).lookup(Button.class));
  77                 for (final Wrap<? extends Button> buttonWrap : allVisibleButtonWraps) {
  78                         Fetcher<Image> fetcher = new Fetcher<Image>() {
  79                                 @Override
  80                                 public void run() {
  81                                         setOutput(buttonWrap.getControl().getImage());
  82                                 }
  83                         };
  84                         Display.getDefault().syncExec(fetcher);
  85                         if (image.equals(fetcher.getOutput())) {
  86                                 return new MCButton(buttonWrap);
  87                         }
  88                 }
  89                 return null;
  90         }
  91 
  92         /**
  93          * Finds a button in the default Mission Control shell and returns it.
  94          *
  95          * @param image
  96          *            the image of the button
  97          * @return a {@link MCButton} in the default shell matching the image.
  98          */
  99         public static MCButton getByImage(Image image) {
 100                 return getByImage(getShell(), image);
 101         }
 102 
 103         /**
 104          * Finds a button in the default Mission Control shell and returns it.
 105          *
 106          * @param label
 107          *            the {@link MCButton} Label of the button
 108          * @return a {@link MCButton} in the default shell matching the label
 109          */
 110         public static MCButton getByLabel(Labels label) {
 111                 return getByLabel(getShell(), label);
 112         }
 113 
 114         /**
 115          * Finds a button in the default Mission Control shell and returns it.
 116          *
 117          * @param label
 118          *            the {@link MCButton} Label of the button
 119          * @param waitForIdle
 120          *            {@code true} if supposed to wait for an idle UI before trying to find the Button
 121          * @return a {@link MCButton} in the default shell matching the label
 122          */
 123         public static MCButton getByLabel(Labels label, boolean waitForIdle) {
 124                 return getByLabel(getShell(), label, waitForIdle);
 125         }
 126 
 127         /**
 128          * Finds a button in the default Mission Control shell and returns it.
 129          *
 130          * @param label
 131          *            the label string of the button
 132          * @return a {@link MCButton} in the default shell matching the label
 133          */
 134         public static MCButton getByLabel(String label) {
 135                 return getByLabel(getShell(), label);
 136         }
 137 
 138         /**
 139          * Finds a button in a shell with the given text and returns it.
 140          *
 141          * @param label
 142          *            the label string of the button
 143          * @param shellText
 144          *            the text to look up the shell that the button is contained in
 145          * @return a {@link MCButton} in the shell matching the label
 146          */
 147         public static MCButton getByLabel(String  shellText, String label) {
 148                 return getByLabel(getShellByText(shellText), label);
 149         }
 150 
 151         /**
 152          * Finds a button by button label and returns it
 153          *
 154          * @param shell
 155          *            the shell where to find the button
 156          * @param label
 157          *            the {@link MCButton} Label of the button
 158          * @param waitForIdle
 159          *            {@code true} if supposed to wait for an idle UI before trying to find the Button
 160          * @return a {@link MCButton} in the correct shell matching the label
 161          */
 162         public static MCButton getByLabel(Wrap<? extends Shell> shell, Labels label, boolean waitForIdle) {
 163                 return getByLabel(shell, Labels.getButtonLabel(label), waitForIdle);
 164         }
 165 
 166         /**
 167          * Finds a button by button label and returns it
 168          *
 169          * @param shell
 170          *            the shell where to find the button
 171          * @param label
 172          *            the {@link MCButton} Label of the button
 173          * @return a {@link MCButton} in the correct shell matching the label
 174          */
 175         public static MCButton getByLabel(Wrap<? extends Shell> shell, Labels label) {
 176                 return getByLabel(shell, Labels.getButtonLabel(label));
 177         }
 178 
 179         /**
 180          * Finds a button by button label string and returns it
 181          *
 182          * @param shell
 183          *            the shell where to find the button
 184          * @param label
 185          *            the label string of the button
 186          * @return a {@link MCButton} in the correct shell matching the label
 187          */
 188         public static MCButton getByLabel(Wrap<? extends Shell> shell, String label) {
 189                 return getByLabel(shell, label, true);
 190         }
 191 
 192         /**
 193          * Finds a button by button label string and returns it
 194          *
 195          * @param shell
 196          *            the shell where to find the button
 197          * @param label
 198          *            the label string of the button
 199          * @param waitForIdle
 200          *            {@code true} if supposed to wait for an idle UI before trying to find the Button
 201          * @return a {@link MCButton} in the correct shell matching the label
 202          */
 203         @SuppressWarnings("unchecked")
 204         public static MCButton getByLabel(Wrap<? extends Shell> shell, String label, boolean waitForIdle) {
 205                 Lookup<Button> lookup = shell.as(Parent.class, Button.class).lookup(Button.class,
 206                                 new ByTextControlLookup<Button>(label));
 207                 return new MCButton(getVisible(lookup, waitForIdle).get(0));
 208         }
 209 
 210         /**
 211          * Finds a button by button label string and returns it
 212          *
 213          * @param dialog
 214          *            the {@link MCDialog} where to find the button
 215          * @param label
 216          *            the label string of the button
 217          * @param waitForIdle
 218          *            {@code true} if supposed to wait for an idle UI before trying to find the Button
 219          * @return a {@link MCButton} in the correct dialog matching the label
 220          */
 221         public static MCButton getByLabel(MCDialog dialog, String label, boolean waitForIdle) {
 222                 return getByLabel(dialog.getDialogShell(), label, waitForIdle);
 223         }
 224 
 225         /**
 226          * Finds a button by button label string and returns it
 227          *
 228          * @param dialog
 229          *            the {@link MCDialog} where to find the button
 230          * @param label
 231          *            the {@link MCButton} Label of the button
 232          * @param waitForIdle
 233          *            {@code true} if supposed to wait for an idle UI before trying to find the Button
 234          * @return a {@link MCButton} in the correct dialog matching the label
 235          */
 236         public static MCButton getByLabel(MCDialog dialog, Labels label, boolean waitForIdle) {
 237                 return getByLabel(dialog, Labels.getButtonLabel(label), waitForIdle);
 238         }
 239 
 240         /**
 241          * Finds a button, visible or not, by button label string and returns it
 242          *
 243          * @param shell
 244          *            the shell where to find the button
 245          * @param label
 246          *            the label string of the button
 247          * @return a {@link MCButton} in the correct shell matching the label, {@code null} if not
 248          *         found
 249          */
 250         @SuppressWarnings("unchecked")
 251         public static MCButton getAnyByLabel(Wrap<? extends Shell> shell, String label) {
 252                 Lookup<Button> lookup = shell.as(Parent.class, Button.class).lookup(Button.class,
 253                                 new ByTextControlLookup<Button>(label));
 254                 if (lookup.size() > 0) {
 255                         return new MCButton(lookup.wrap(0));
 256                 } else {
 257                         return null;
 258                 }
 259         }
 260 
 261         /**
 262          * Finds a button, visible or not, by name
 263          *
 264          * @param shell
 265          *            the shell where to find the button
 266          * @param name
 267          *            the name of the button
 268          * @return a {@link MCButton} matching the name, {@code null} if not found
 269          */
 270         @SuppressWarnings("unchecked")
 271         public static MCButton getByName(Wrap<? extends Shell> shell, String name) {
 272                 return new MCButton(shell.as(Parent.class, Button.class).lookup(Button.class, new ByName<>(name)).wrap());
 273         }
 274 
 275         /**
 276          * Finds a button, visible or not, by name (in the main shell of Mission Control)
 277          *
 278          * @param name
 279          *            the name of the button
 280          * @return a {@link MCButton} matching the name, {@code null} if not found
 281          */
 282         public static MCButton getByName(String name) {
 283                 return getByName(getShell(), name);
 284         }
 285 
 286         /**
 287          * Finds all visible buttons in the supplied shell and returns a {@link List} of these
 288          *
 289          * @param shell
 290          *            the shell where to search for buttons
 291          * @return a {@link List} of {@link MCButton} (possibly empty)
 292          */
 293         @SuppressWarnings("unchecked")
 294         public static List<MCButton> getVisible(Wrap<? extends Shell> shell) {
 295                 List<Wrap<? extends Button>> allVisibleButtonWraps = getVisible(
 296                                 shell.as(Parent.class, Button.class).lookup(Button.class));
 297                 List<MCButton> allVisibleMcButtons = new ArrayList<>();
 298                 for (Wrap<? extends Button> buttonWrap : allVisibleButtonWraps) {
 299                         allVisibleMcButtons.add(new MCButton(buttonWrap));
 300                 }
 301                 return allVisibleMcButtons;
 302         }
 303 
 304         /**
 305          * Finds all visible buttons in the supplied shell and returns a {@link List} of these
 306          *
 307          * @param shell
 308          *            the shell where to search for buttons
 309          * @param waitForIdle
 310          *            {@code true} if supposed to wait for the UI to be idle before ending the lookup
 311          * @return a {@link List} of {@link MCButton} (possibly empty)
 312          */
 313         @SuppressWarnings("unchecked")
 314         public static List<MCButton> getVisible(Wrap<? extends Shell> shell, boolean waitForIdle) {
 315                 List<Wrap<? extends Button>> allVisibleButtonWraps = getVisible(
 316                                 shell.as(Parent.class, Button.class).lookup(Button.class), waitForIdle);
 317                 List<MCButton> allVisibleMcButtons = new ArrayList<>();
 318                 for (Wrap<? extends Button> buttonWrap : allVisibleButtonWraps) {
 319                         allVisibleMcButtons.add(new MCButton(buttonWrap));
 320                 }
 321                 return allVisibleMcButtons;
 322         }
 323 
 324         /**
 325          * Gets the selection state of the button.
 326          *
 327          * @return {@code true} if selected, otherwise {@code false}
 328          */
 329         public boolean getSelection() {
 330                 Fetcher<Boolean> fetcher = new Fetcher<Boolean>() {
 331                         @Override
 332                         public void run() {
 333                                 setOutput(getWrap().getControl().getSelection());
 334                         }
 335                 };
 336                 Display.getDefault().syncExec(fetcher);
 337                 return fetcher.getOutput();
 338         }
 339 
 340         /**
 341          * Sets the state of the button/checkbox with retries in case it is a checkbox that may be grey.
 342          * Sets the click point very close to the origin of the button instead of centered in order to
 343          * ensure that Mac OS X will work as well
 344          *
 345          * @param state
 346          *            the desired state of the button/checkbox
 347          */
 348         public void setState(boolean state) {
 349                 int maxRetries = 10;
 350                 int currentRetry = 0;
 351                 while (getSelection() != state && maxRetries > currentRetry) {
 352                         currentRetry++;
 353                         control.mouse().click(1, new Point(1, 1));
 354                         sleep(200);
 355                 }
 356                 Assert.assertTrue("Unable to set Button state to " + state, getSelection() == state);
 357         }
 358 
 359         public static enum Labels {
 360                 OK, FINISH, CANCEL, CLOSE, YES, NEXT, NO, APPLY_AND_CLOSE;
 361 
 362                 public static String getButtonLabel(Labels buttonLabel) {
 363                         String labelString = "";
 364 
 365                         switch (buttonLabel) {
 366                         case YES:
 367                                 labelString = IDialogConstants.YES_LABEL;
 368                                 break;
 369                         case CANCEL:
 370                                 labelString = IDialogConstants.CANCEL_LABEL;
 371                                 break;
 372                         case CLOSE:
 373                                 labelString = IDialogConstants.CLOSE_LABEL;
 374                                 break;
 375                         case FINISH:
 376                                 labelString = IDialogConstants.FINISH_LABEL;
 377                                 break;
 378                         case NEXT:
 379                                 labelString = IDialogConstants.NEXT_LABEL;
 380                                 break;
 381                         case OK:
 382                                 labelString = IDialogConstants.OK_LABEL;
 383                                 break;
 384                         case NO:
 385                                 labelString = IDialogConstants.NO_LABEL;
 386                                 break;
 387                         case APPLY_AND_CLOSE:
 388                                 labelString = "Apply and Close";
 389                         }
 390                         return labelString;
 391                 }
 392         }
 393 
 394         @SuppressWarnings("unchecked")
 395         private Wrap<? extends Button> getWrap() {
 396                 return control.as(ControlWrap.class);
 397         }
 398 }