1 /*
   2  * Copyright (c) 2010, 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 
  26 package javafx.scene.control;
  27 
  28 import javafx.beans.property.BooleanProperty;
  29 import javafx.beans.property.SimpleBooleanProperty;
  30 import javafx.event.ActionEvent;
  31 import javafx.event.EventHandler;
  32 import javafx.scene.Scene;
  33 import javafx.scene.input.KeyCode;
  34 import javafx.scene.input.MouseButton;
  35 import javafx.scene.layout.BackgroundFill;
  36 import javafx.scene.layout.HBox;
  37 import javafx.scene.layout.Region;
  38 import javafx.scene.layout.StackPane;
  39 import javafx.scene.paint.LinearGradient;
  40 import javafx.scene.paint.Stop;
  41 import javafx.scene.shape.Rectangle;
  42 import javafx.stage.Stage;
  43 import javafx.stage.WindowEvent;
  44 import java.util.List;
  45 import com.sun.javafx.pgstub.StubToolkit;
  46 import com.sun.javafx.scene.control.infrastructure.ContextMenuEventFirer;
  47 import com.sun.javafx.scene.control.infrastructure.KeyEventFirer;
  48 import com.sun.javafx.scene.control.infrastructure.MouseEventFirer;
  49 import com.sun.javafx.tk.Toolkit;
  50 import org.junit.Before;
  51 import org.junit.After;
  52 import org.junit.Ignore;
  53 import org.junit.Test;
  54 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertPseudoClassDoesNotExist;
  55 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertPseudoClassExists;
  56 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains;
  57 import static org.junit.Assert.assertEquals;
  58 import static org.junit.Assert.assertFalse;
  59 import static org.junit.Assert.assertNull;
  60 import static org.junit.Assert.assertSame;
  61 import static org.junit.Assert.assertTrue;
  62 import static org.junit.Assert.fail;
  63 
  64 //import com.sun.javafx.test.MouseEventGenerator;
  65 
  66 /**
  67  * action (which can be bound, and can be null),
  68  * and that action is called when the button is fired.
  69  */
  70 public class ButtonTest {
  71     private Button btn;
  72     private Toolkit tk;
  73     private Scene scene;
  74     private Stage stage;
  75     private StackPane root;
  76     private MouseEventFirer mouse;
  77     
  78     @Before public void setup() {
  79         btn = new Button();
  80         tk = (StubToolkit)Toolkit.getToolkit();//This step is not needed (Just to make sure StubToolkit is loaded into VM)
  81         root = new StackPane();
  82         scene = new Scene(root);
  83         stage = new Stage();
  84         stage.setScene(scene);
  85         mouse = new MouseEventFirer(btn);
  86     }
  87 
  88     @After public void after() {
  89         stage.hide();
  90         mouse.dispose();
  91     }
  92     
  93     /*********************************************************************
  94      * Helper methods                                                    *
  95      ********************************************************************/
  96     private void show() {
  97         stage.show();
  98     }   
  99     
 100     /*********************************************************************
 101      * Tests for the constructors                                        *
 102      ********************************************************************/
 103     
 104     @Test public void defaultConstructorShouldHaveNoGraphicAndEmptyString() {
 105         assertNull(btn.getGraphic());
 106         assertEquals("", btn.getText());
 107     }
 108     
 109     @Test public void oneArgConstructorShouldHaveNoGraphicAndSpecifiedString() {
 110         Button b2 = new Button(null);
 111         assertNull(b2.getGraphic());
 112         assertNull(b2.getText());
 113         
 114         b2 = new Button("");
 115         assertNull(b2.getGraphic());
 116         assertEquals("", b2.getText());
 117         
 118         b2 = new Button("Hello");
 119         assertNull(b2.getGraphic());
 120         assertEquals("Hello", b2.getText());
 121     }
 122     
 123     @Test public void twoArgConstructorShouldHaveSpecifiedGraphicAndSpecifiedString() {
 124         Button b2 = new Button(null, null);
 125         assertNull(b2.getGraphic());
 126         assertNull(b2.getText());
 127 
 128         Rectangle rect = new Rectangle();
 129         b2 = new Button("Hello", rect);
 130         assertSame(rect, b2.getGraphic());
 131         assertEquals("Hello", b2.getText());
 132     }
 133 
 134     @Test public void defaultConstructorShouldSetStyleClassTo_button() {
 135         assertStyleClassContains(btn, "button");
 136     }
 137     
 138     @Test public void oneArgConstructorShouldSetStyleClassTo_button() {
 139         Button b2 = new Button(null);
 140         assertStyleClassContains(b2, "button");
 141     }
 142     
 143     @Test public void twoArgConstructorShouldSetStyleClassTo_button() {
 144         Button b2 = new Button(null, null);
 145         assertStyleClassContains(b2, "button");
 146     }
 147     
 148     /*********************************************************************
 149      * Tests for the defaultButton state                                 *
 150      ********************************************************************/
 151 
 152     @Test public void defaultButtonIsFalseByDefault() {
 153         assertFalse(btn.isDefaultButton());
 154         assertFalse(btn.defaultButtonProperty().getValue());
 155     }
 156 
 157     @Test public void defaultButtonCanBeSet() {
 158         btn.setDefaultButton(true);
 159         assertTrue(btn.isDefaultButton());
 160     }
 161 
 162     @Test public void defaultButtonSetToNonDefaultValueIsReflectedInModel() {
 163         btn.setDefaultButton(true);
 164         assertTrue(btn.defaultButtonProperty().getValue());
 165     }
 166 
 167     @Test public void defaultButtonCanBeCleared() {
 168         btn.setDefaultButton(true);
 169         btn.setDefaultButton(false);
 170         assertFalse(btn.isDefaultButton());
 171     }
 172 
 173     @Test public void defaultButtonCanBeBound() {
 174         BooleanProperty other = new SimpleBooleanProperty(true);
 175         btn.defaultButtonProperty().bind(other);
 176         assertTrue(btn.isDefaultButton());
 177     }
 178 
 179     @Test public void settingDefaultButtonSetsPseudoClass() {
 180         btn.setDefaultButton(true);
 181         assertPseudoClassExists(btn, "default");
 182     }
 183 
 184     @Test public void clearingDefaultButtonClearsPseudoClass() {
 185         btn.setDefaultButton(true);
 186         btn.setDefaultButton(false);
 187         assertPseudoClassDoesNotExist(btn, "default");
 188     }
 189 
 190     @Test public void defaultButtonSetToTrueViaBindingSetsPseudoClass() {
 191         BooleanProperty other = new SimpleBooleanProperty(true);
 192         btn.defaultButtonProperty().bind(other);
 193         assertPseudoClassExists(btn, "default");
 194     }
 195 
 196     @Test public void defaultButtonSetToFalseViaBindingClearsPseudoClass() {
 197         BooleanProperty other = new SimpleBooleanProperty(true);
 198         btn.defaultButtonProperty().bind(other);
 199         other.setValue(false);
 200         assertPseudoClassDoesNotExist(btn, "default");
 201     }
 202 
 203     @Ignore("impl_cssSet API removed")
 204     @Test public void cannotSpecifyDefaultButtonViaCSS() {
 205 //        btn.impl_cssSet("-fx-default-button", true);
 206         assertFalse(btn.isDefaultButton());
 207     }
 208 
 209     @Test public void defaultButtonPropertyHasBeanReference() {
 210         assertSame(btn, btn.defaultButtonProperty().getBean());
 211     }
 212 
 213     @Test public void defaultButtonPropertyHasName() {
 214         assertEquals("defaultButton", btn.defaultButtonProperty().getName());
 215     }
 216     
 217     @Test public void disabledDefaultButtonCannotGetInvoked_RT20929() {
 218         root.getChildren().add(btn);        
 219         
 220         btn.setOnAction(actionEvent -> {
 221             fail();
 222         });
 223         
 224         btn.setDefaultButton(true);
 225         btn.setDisable(true);
 226         show();
 227         
 228         KeyEventFirer keyboard = new KeyEventFirer(btn);        
 229         keyboard.doKeyPress(KeyCode.ENTER);
 230    
 231         tk.firePulse();                
 232     }
 233 
 234     @Test public void defaultButtonCanBeInvokeAfterRemovingFromTheScene_RT22106() {
 235         btn.setDefaultButton(true);        
 236         btn.setOnAction(actionEvent -> {
 237             fail();
 238         });
 239         root.getChildren().add(btn);
 240         show();
 241         
 242         root.getChildren().remove(btn);        
 243         
 244         KeyEventFirer keyboard = new KeyEventFirer(root);        
 245         keyboard.doKeyPress(KeyCode.ENTER);
 246 
 247         tk.firePulse();                      
 248     }
 249     
 250     /*********************************************************************
 251      * Tests for the cancelButton state                                 *
 252      ********************************************************************/
 253 
 254     @Test public void cancelButtonIsFalseByDefault() {
 255         assertFalse(btn.isCancelButton());
 256         assertFalse(btn.cancelButtonProperty().getValue());
 257     }
 258 
 259     @Test public void cancelButtonCanBeSet() {
 260         btn.setCancelButton(true);
 261         assertTrue(btn.isCancelButton());
 262     }
 263 
 264     @Test public void cancelButtonSetToNonDefaultValueIsReflectedInModel() {
 265         btn.setCancelButton(true);
 266         assertTrue(btn.cancelButtonProperty().getValue());
 267     }
 268 
 269     @Test public void cancelButtonCanBeCleared() {
 270         btn.setCancelButton(true);
 271         btn.setCancelButton(false);
 272         assertFalse(btn.isCancelButton());
 273     }
 274 
 275     @Test public void cancelButtonCanBeBound() {
 276         BooleanProperty other = new SimpleBooleanProperty(true);
 277         btn.cancelButtonProperty().bind(other);
 278         assertTrue(btn.isCancelButton());
 279     }
 280 
 281     @Test public void settingCancelButtonSetsPseudoClass() {
 282         btn.setCancelButton(true);
 283         assertPseudoClassExists(btn, "cancel");
 284     }
 285 
 286     @Test public void clearingCancelButtonClearsPseudoClass() {
 287         btn.setCancelButton(true);
 288         btn.setCancelButton(false);
 289         assertPseudoClassDoesNotExist(btn, "cancel");
 290     }
 291 
 292     @Test public void cancelButtonSetToTrueViaBindingSetsPseudoClass() {
 293         BooleanProperty other = new SimpleBooleanProperty(true);
 294         btn.cancelButtonProperty().bind(other);
 295         assertPseudoClassExists(btn, "cancel");
 296     }
 297 
 298     @Test public void cancelButtonSetToFalseViaBindingClearsPseudoClass() {
 299         BooleanProperty other = new SimpleBooleanProperty(true);
 300         btn.cancelButtonProperty().bind(other);
 301         other.setValue(false);
 302         assertPseudoClassDoesNotExist(btn, "cancel");
 303     }
 304 
 305     @Ignore("impl_cssSet API removed")
 306     @Test public void cannotSpecifyCancelButtonViaCSS() {
 307 //        btn.impl_cssSet("-fx-cancel-button", true);
 308         assertFalse(btn.isCancelButton());
 309     }
 310 
 311     @Test public void cancelButtonPropertyHasBeanReference() {
 312         assertSame(btn, btn.cancelButtonProperty().getBean());
 313     }
 314 
 315     @Test public void cancelButtonPropertyHasName() {
 316         assertEquals("cancelButton", btn.cancelButtonProperty().getName());
 317     }
 318 
 319     @Test public void cancelButtonCanBeInvokeAfterRemovingFromTheScene_RT22106() {
 320         btn.setCancelButton(true);        
 321         btn.setOnAction(actionEvent -> {
 322             fail();
 323         });
 324         root.getChildren().add(btn);
 325         show();
 326         
 327         root.getChildren().remove(btn);        
 328         
 329         KeyEventFirer keyboard = new KeyEventFirer(root);        
 330         keyboard.doKeyPress(KeyCode.ESCAPE);
 331 
 332         tk.firePulse();                      
 333     }
 334 
 335 
 336     @Test public void conextMenuShouldntShowOnAction() {
 337         ContextMenu popupMenu = new ContextMenu();
 338         MenuItem item1 = new MenuItem("_About");
 339         popupMenu.getItems().add(item1);
 340         popupMenu.setOnShown(w -> {
 341             fail();
 342         });
 343 
 344         btn.setContextMenu(popupMenu);
 345         btn.setDefaultButton(true);
 346 
 347         root.getChildren().add(btn);
 348         show();
 349 
 350         // None of these should cause the context menu to appear,
 351         // so fire them all, and see if anything happens.
 352         KeyEventFirer keyboard = new KeyEventFirer(btn);
 353         keyboard.doKeyPress(KeyCode.ENTER);
 354 
 355         btn.fireEvent(new ActionEvent());
 356         btn.fire();
 357 
 358         mouse.fireMousePressed();
 359         mouse.fireMouseReleased();
 360         mouse.fireMouseClicked();
 361     }
 362     
 363     private int count = 0;
 364     @Test public void contextMenuShouldShowOnInSomeCircumstances() {
 365         ContextMenu popupMenu = new ContextMenu();
 366         MenuItem item1 = new MenuItem("_About");
 367         popupMenu.getItems().add(item1);
 368         popupMenu.setOnShown(w -> {
 369             System.out.println("popup shown");
 370             count++;
 371         });
 372 
 373         btn.setContextMenu(popupMenu);
 374         btn.setDefaultButton(true);
 375 
 376         root.getChildren().add(btn);
 377         show();
 378         
 379         btn.setOnAction(event -> {
 380             fail();
 381         });
 382 
 383         assertEquals(0, count);
 384         
 385         /* Note that right-mouse press events don't force the popup open */
 386         mouse.fireMousePressed(MouseButton.SECONDARY);
 387         assertEquals(0, count);
 388         
 389         mouse.fireMouseClicked(MouseButton.SECONDARY);
 390         assertEquals(0, count);
 391         
 392         mouse.fireMouseReleased(MouseButton.SECONDARY);
 393         assertEquals(0, count);
 394         
 395         /* Only context menu events force it to appear */
 396         ContextMenuEventFirer.fireContextMenuEvent(btn);
 397         assertEquals(1, count);
 398     }
 399 
 400     static class MyButton extends Button {
 401         MyButton(String text) {
 402             super(text);
 403         }
 404         
 405         void setHoverPseudoclassState(boolean b) {
 406             setHover(b);
 407         }
 408     }    
 409     
 410     List<Stop> getStops(Button button) {
 411         Skin skin = button.getSkin();
 412         Region region = (Region)skin.getNode();
 413         List<BackgroundFill> fills = region.getBackground().getFills();
 414         BackgroundFill top = fills.get(fills.size()-1);
 415         LinearGradient topFill = (LinearGradient)top.getFill();
 416         return topFill.getStops();
 417     }
 418 
 419     @Test
 420     public void testRT_23207() {
 421         
 422         HBox hBox = new HBox();
 423         hBox.setSpacing(5);
 424         hBox.setTranslateY(30);
 425         MyButton red = new MyButton("Red");
 426         red.setStyle("-fx-base: red;");
 427         MyButton green = new MyButton("Green");
 428         green.setStyle("-fx-base: green;");
 429         hBox.getChildren().add(red);
 430         hBox.getChildren().add(green);
 431                 
 432         Scene scene = new Scene(hBox, 500, 300);
 433         Stage stage = new Stage();
 434         stage.setScene(scene);
 435         stage.show();
 436         
 437         Toolkit.getToolkit().firePulse();
 438         
 439         List<Stop> redStops0 = getStops(red);
 440         List<Stop> greenStops0 = getStops(green);
 441         
 442         red.setHoverPseudoclassState(true);
 443         
 444         Toolkit.getToolkit().firePulse();
 445 
 446         List<Stop> redStops1 = getStops(red);
 447         List<Stop> greenStops1 = getStops(green);
 448                 
 449         red.setHoverPseudoclassState(false);
 450         green.setHoverPseudoclassState(true);
 451         
 452         Toolkit.getToolkit().firePulse();
 453         
 454         List<Stop> redStops2 = getStops(red);
 455         List<Stop> greenStops2 = getStops(green);
 456 
 457         green.setHoverPseudoclassState(false);
 458         
 459         Toolkit.getToolkit().firePulse();
 460 
 461         List<Stop> redStops3 = getStops(red);
 462         List<Stop> greenStops3 = getStops(green);
 463 
 464         // did red change color after red hover=true?
 465         assertFalse(redStops0.equals(redStops1));
 466         // did red change back to original color after green hover=true?
 467         assertTrue(redStops0.equals(redStops2));
 468         // did red stay original color after green hover=false?
 469         assertTrue(redStops0.equals(redStops3));
 470         // did green stay green after red hover=true?
 471         assertTrue(greenStops0.equals(greenStops1));
 472         // did green change after green hover=true?
 473         assertFalse(greenStops0.equals(greenStops2));
 474         // did green revert to original after green hover=false?
 475         // This is the acid test. If this fails, then RT-23207 is present.
 476         assertTrue(greenStops0.equals(greenStops3));
 477         
 478     }    
 479 
 480     
 481 //  private Button button1;
 482 //  private Button button2;
 483 //
 484 //  @Override protected Node createNodeToTest() {
 485 //    button1 = createButton("Button1");
 486 //    button2 = createButton("Button2");
 487 //    button2.setLayoutX(0);
 488 //    button2.setLayoutY(110);
 489 //    Group group = new Group();
 490 //    group.getChildren().addAll(button1, button2);
 491 //    group.setAutoSizeChildren(false);
 492 //    return group;
 493 //  }
 494 //
 495 //  private static Button createButton(String text) {
 496 //    Button button = new Button(text);
 497 //    button.resize(100, 100);
 498 //    return button;
 499 //  }
 500 //
 501 //  @Test public void pressEventsShouldLeadToFocusGained() {
 502 //    mouse().positionAtCenterOf(button2);
 503 //    mouse().leftPress();
 504 //    assertTrue(button2.isFocused());
 505 //  }
 506 //
 507 //  @Test public void pressEventsShouldLeadToFocusGained_efficiently() {
 508 //    executeInUIThread(new Runnable() {
 509 //      @Override public void run() {
 510 //        mouse().positionAtCenterOf(button2);
 511 //        mouse().leftPress();
 512 //      }
 513 //    });
 514 //    assertTrue(button2.isFocused());
 515 //  }
 516 }