1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package test.javafx.scene.control;
  26 
  27 import javafx.application.Platform;
  28 import javafx.scene.control.Alert;
  29 import javafx.scene.control.ButtonType;
  30 import javafx.scene.control.Dialog;
  31 import javafx.scene.control.DialogShim;
  32 import javafx.scene.control.HeavyweightDialogShim;
  33 import javafx.stage.Stage;
  34 import javafx.stage.WindowEvent;
  35 import org.junit.After;
  36 import org.junit.Test;
  37 
  38 import static org.junit.Assert.*;
  39 
  40 /**
  41  * Note that this class tests non-blocking alerts only. For blocking alerts,
  42  * there exists an exact copy of this code in
  43  * rt\tests\system\src\test\java\javafx\scene\control
  44  *
  45  * Whenever this file changes, the contents of the class should be copied over
  46  * to the system test class __with no changes__.
  47  */
  48 public class AlertTest {
  49 
  50     private static final String DUMMY_RESULT = "dummy";
  51     static boolean blocking = false;
  52 
  53     private Dialog<ButtonType> dialog;
  54 
  55     private boolean closeWasForcedButStageWasShowing = false;
  56     private boolean closeVetoed = false;
  57     private Object result = DUMMY_RESULT;
  58 
  59     @After public void cleanup() {
  60         getStage(dialog).close();
  61         dialog = null;
  62         result = DUMMY_RESULT;
  63         closeVetoed = false;
  64         closeWasForcedButStageWasShowing = false;
  65     }
  66 
  67     private static Stage getStage(Dialog<?> dialog) {
  68         return HeavyweightDialogShim.get_stage(DialogShim.get_dialog(dialog));
  69     }
  70 
  71     private void showAndHideDialog(Dialog<?> dialog, boolean normalClose) {
  72         if (dialog.isShowing()) return;
  73 
  74         if (blocking) {
  75             new Thread(() -> {
  76                 try {
  77                     // wait a short while after showing the dialog and try to
  78                     // close it.
  79                     Thread.sleep(750);
  80                     Platform.runLater(() -> hideDialog(dialog, normalClose));
  81 
  82                     // wait again
  83                     Thread.sleep(750);
  84                     if (closeVetoed) {
  85                         // now we get serious and clobber the stage so that we
  86                         // can carry on with the next test.
  87                         result = dialog.getResult();
  88                         closeWasForcedButStageWasShowing = true;
  89                         Platform.runLater(() -> getStage(dialog).close());
  90                     }
  91                 } catch (InterruptedException e) {
  92                     e.printStackTrace();
  93                 }
  94             }).start();
  95 
  96             dialog.showAndWait();
  97         } else {
  98             dialog.show();
  99             hideDialog(dialog, normalClose);
 100         }
 101     }
 102 
 103     private void hideDialog(Dialog<?> dialog, boolean normalClose) {
 104         Stage stage = getStage(dialog);
 105         WindowEvent event = new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST);
 106 
 107         if (normalClose) {
 108             // we can't just click 'dialog.close()' here
 109             dialog.close();
 110         } else {
 111             // this is hacky, but effectively we're depending on the implementation
 112             // detail of heavyweight dialogs to call into the onCloseRequest
 113             // handler of the heavyweight stage.
 114             stage.getOnCloseRequest().handle(event);
 115         }
 116 
 117         // if at this point the dialog is still showing, then the close was obviously vetoed
 118         closeVetoed = dialog.isShowing();
 119     }
 120 
 121     private void assertResultValue(Object expected, Dialog<?> dialog, boolean normalClose) {
 122         showAndHideDialog(dialog, normalClose);
 123 
 124         if (result != DUMMY_RESULT) {
 125             assertEquals(expected, result);
 126         } else {
 127             assertEquals(expected, dialog.getResult());
 128         }
 129     }
 130 
 131     private void assertCloseRequestVetoed(Dialog<?> dialog, boolean normalClose) {
 132         Stage stage = getStage(dialog);
 133         showAndHideDialog(dialog, normalClose);
 134 
 135         assertTrue(closeWasForcedButStageWasShowing || stage.isShowing());
 136         assertTrue(closeWasForcedButStageWasShowing || dialog.isShowing());
 137     }
 138 
 139     private void assertCloseRequestAccepted(Dialog<?> dialog, boolean normalClose) {
 140         Stage stage = getStage(dialog);
 141         showAndHideDialog(dialog, normalClose);
 142 
 143         assertFalse(!closeWasForcedButStageWasShowing && stage.isShowing());
 144         assertFalse(!closeWasForcedButStageWasShowing && dialog.isShowing());
 145     }
 146 
 147 
 148     // --- Information alert tests
 149     // Information has one 'OK' button.
 150     // Because there is no cancel button, but only one button is present, we
 151     // can close the dialog without veto, and the result will be null.
 152     //
 153     // TODO review the above statement - should we return null, ButtonType.CANCEL
 154     // (even though it doesn't exist in the dialog) or, in cases where there is
 155     // only one button, do we return that button as the result?
 156     @Test public void alert_information_abnormalClose() {
 157         dialog = new Alert(Alert.AlertType.INFORMATION, "Hello World!");
 158         assertResultValue(null, dialog, false);
 159         assertCloseRequestAccepted(dialog, false);
 160     }
 161 
 162     @Test public void alert_information_normalClose() {
 163         dialog = new Alert(Alert.AlertType.INFORMATION, "Hello World!");
 164         assertResultValue(null, dialog, true);
 165         assertCloseRequestAccepted(dialog, true);
 166     }
 167 
 168 
 169     // --- Error alert tests
 170     // Error has one 'OK' button.
 171     // Because there is no cancel button, but only one button is present, we
 172     // can close the dialog without veto, and the result will be null.
 173     //
 174     // TODO review the above statement - should we return null, ButtonType.CANCEL
 175     // (even though it doesn't exist in the dialog) or, in cases where there is
 176     // only one button, do we return that button as the result?
 177     @Test public void alert_error_abnormalClose() {
 178         dialog = new Alert(Alert.AlertType.ERROR, "Hello World!");
 179         assertResultValue(null, dialog, false);
 180         assertCloseRequestAccepted(dialog, false);
 181     }
 182 
 183     @Test public void alert_error_normalClose() {
 184         dialog = new Alert(Alert.AlertType.ERROR, "Hello World!");
 185         assertResultValue(null, dialog, true);
 186         assertCloseRequestAccepted(dialog, true);
 187     }
 188 
 189 
 190     // --- Warning alert tests
 191     // Warning has one 'OK' button.
 192     // Because there is no cancel button, but only one button is present, we
 193     // can close the dialog without veto, and the result will be null.
 194     //
 195     // TODO review the above statement - should we return null, ButtonType.CANCEL
 196     // (even though it doesn't exist in the dialog) or, in cases where there is
 197     // only one button, do we return that button as the result?
 198     @Test public void alert_warning_abnormalClose() {
 199         dialog = new Alert(Alert.AlertType.WARNING, "Hello World!");
 200         assertResultValue(null, dialog, false);
 201         assertCloseRequestAccepted(dialog, false);
 202     }
 203 
 204     @Test public void alert_warning_normalClose() {
 205         dialog = new Alert(Alert.AlertType.WARNING, "Hello World!");
 206         assertResultValue(null, dialog, true);
 207         assertCloseRequestAccepted(dialog, true);
 208     }
 209 
 210 
 211     // --- Confirmation alert tests
 212     // Confirmation has two buttons: 'OK' and 'Cancel'
 213     // Because there is a cancel button, close requests are accepted, and the
 214     // result type is ButtonType.CANCEL
 215     @Test public void alert_confirmation_abnormalClose() {
 216         dialog = new Alert(Alert.AlertType.CONFIRMATION, "Hello World!");
 217         assertResultValue(ButtonType.CANCEL, dialog, false);
 218         assertCloseRequestAccepted(dialog, false);
 219     }
 220 
 221     @Test public void alert_confirmation_normalClose() {
 222         dialog = new Alert(Alert.AlertType.CONFIRMATION, "Hello World!");
 223         assertResultValue(ButtonType.CANCEL, dialog, true);
 224         assertCloseRequestAccepted(dialog, true);
 225     }
 226 
 227 
 228     // --- AlertType.NONE alert tests
 229     // None has no buttons
 230     // Because there is no cancel button, and zero other buttons, this dialog by
 231     // default should not be closable
 232     @Test public void alert_none_abnormalClose() {
 233         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 234         assertResultValue(null, dialog, false);
 235         assertCloseRequestVetoed(dialog, false);
 236     }
 237 
 238     @Test public void alert_none_normalClose() {
 239         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 240         assertResultValue(null, dialog, true);
 241         assertCloseRequestVetoed(dialog, true);
 242     }
 243 
 244 
 245     // --- Testing what happens with custom buttons and closing
 246     @Test public void alert_zeroButtons_abnormalClose() {
 247         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 248         assertResultValue(null, dialog, false);
 249         assertCloseRequestVetoed(dialog, false);
 250     }
 251 
 252     @Test public void alert_zeroButtons_normalClose() {
 253         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 254         assertResultValue(null, dialog, true);
 255         assertCloseRequestVetoed(dialog, true);
 256     }
 257 
 258     @Test public void alert_oneButton_noCancel_abnormalClose() {
 259         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 260         dialog.getDialogPane().getButtonTypes().add(ButtonType.YES);
 261         assertResultValue(null, dialog, false);
 262         assertCloseRequestAccepted(dialog, false);
 263     }
 264 
 265     @Test public void alert_oneButton_noCancel_normalClose() {
 266         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 267         dialog.getDialogPane().getButtonTypes().add(ButtonType.YES);
 268         assertResultValue(null, dialog, true);
 269         assertCloseRequestAccepted(dialog, true);
 270     }
 271 
 272     @Test public void alert_oneButton_withCancel_abnormalClose() {
 273         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 274         dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
 275         assertResultValue(ButtonType.CANCEL, dialog, false);
 276         assertCloseRequestAccepted(dialog, false);
 277     }
 278 
 279     @Test public void alert_oneButton_withCancel_normalClose() {
 280         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 281         dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
 282         assertResultValue(ButtonType.CANCEL, dialog, true);
 283         assertCloseRequestAccepted(dialog, true);
 284     }
 285 
 286     @Test public void alert_twoButtons_noCancel_abnormalClose() {
 287         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 288         dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.YES);
 289         assertResultValue(null, dialog, false);
 290         assertCloseRequestVetoed(dialog, false);
 291     }
 292 
 293     @Test public void alert_twoButtons_noCancel_normalClose() {
 294         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 295         dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.YES);
 296         assertResultValue(null, dialog, true);
 297         assertCloseRequestVetoed(dialog, true);
 298     }
 299 
 300     @Test public void alert_twoButtons_withCancel_abnormalClose() {
 301         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 302         dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL);
 303         assertResultValue(ButtonType.CANCEL, dialog, false);
 304         assertCloseRequestAccepted(dialog, false);
 305     }
 306 
 307     @Test public void alert_twoButtons_withCancel_normalClose() {
 308         dialog = new Alert(Alert.AlertType.NONE, "Hello World!");
 309         dialog.getDialogPane().getButtonTypes().addAll(ButtonType.APPLY, ButtonType.CANCEL);
 310         assertResultValue(ButtonType.CANCEL, dialog, true);
 311         assertCloseRequestAccepted(dialog, true);
 312     }
 313 }