changeset 11015:8451c9f32969

8197846: ComboBox: becomes unclickable after removal and re-adding Reviewed-by: kcr, fastegal
author arapte
date Fri, 03 Aug 2018 17:01:47 +0530
parents 9ad45ae7a5b0
children 5c87692d0056 a3e180fb1506
files modules/javafx.controls/src/main/java/javafx/scene/control/skin/ColorPickerSkin.java modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxListViewSkin.java modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java modules/javafx.controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java tests/system/src/test/java/test/robot/javafx/scene/ColorPickerTest.java tests/system/src/test/java/test/robot/javafx/scene/ComboBoxTest.java tests/system/src/test/java/test/robot/javafx/scene/DatePickerTest.java
diffstat 7 files changed, 648 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ColorPickerSkin.java	Thu Aug 02 11:18:26 2018 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ColorPickerSkin.java	Fri Aug 03 17:01:47 2018 +0530
@@ -129,6 +129,10 @@
 
         pickerColorBox.getChildren().add(colorRect);
         displayNode.setGraphic(pickerColorBox);
+
+        if (control.isShowing()) {
+            show();
+        }
     }
 
 
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxListViewSkin.java	Thu Aug 02 11:18:26 2018 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxListViewSkin.java	Fri Aug 03 17:01:47 2018 +0530
@@ -34,6 +34,7 @@
 import javafx.beans.WeakInvalidationListener;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ListChangeListener;
 import javafx.collections.ObservableList;
@@ -190,6 +191,11 @@
         if (comboBox.isShowing()) {
             show();
         }
+        comboBox.sceneProperty().addListener(o -> {
+            if (((ObservableValue)o).getValue() == null) {
+                comboBox.hide();
+            }
+        });
     }
 
 
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java	Thu Aug 02 11:18:26 2018 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java	Fri Aug 03 17:01:47 2018 +0530
@@ -501,6 +501,8 @@
         getSkinnable().sceneProperty().addListener(o -> {
             if (((ObservableValue)o).getValue() == null) {
                 hide();
+            } else if (getSkinnable().isShowing()) {
+                show();
             }
         });
 
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java	Thu Aug 02 11:18:26 2018 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java	Fri Aug 03 17:01:47 2018 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -147,6 +147,10 @@
                 hide();
             }
         });
+
+        if (control.isShowing()) {
+            show();
+        }
     }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/robot/javafx/scene/ColorPickerTest.java	Fri Aug 03 17:01:47 2018 +0530
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.robot.javafx.scene;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.ColorPicker;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.VBox;
+import javafx.scene.robot.Robot;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.WindowEvent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import test.util.Util;
+
+public class ColorPickerTest {
+
+    static CountDownLatch startupLatch = new CountDownLatch(1);
+    static Robot robot;
+    static volatile Stage stage;
+    static volatile Scene scene;
+    static final int SCENE_WIDTH = 250;
+    static final int SCENE_HEIGHT = SCENE_WIDTH;
+    static VBox root;
+    int onShownCount = 0;
+    int onActionCount = 0;
+    ColorPicker colorPicker;
+    CountDownLatch onShownLatch;
+    CountDownLatch onActionLatch;
+
+    private void mouseClick(double x, double y) {
+        Util.runAndWait(() -> {
+            robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + x),
+                    (int) (scene.getWindow().getY() + scene.getY() + y));
+            robot.mousePress(MouseButton.PRIMARY);
+            robot.mouseRelease(MouseButton.PRIMARY);
+        });
+    }
+
+    private void showColorPickerPalette() throws Exception {
+        onShownLatch = new CountDownLatch(1);
+        mouseClick(colorPicker.getLayoutX() + colorPicker.getWidth() - 15,
+                    colorPicker.getLayoutY() + colorPicker.getHeight() / 2);
+        Thread.sleep(400); // ColorPicker takes some time to display the color palette.
+        waitForLatch(onShownLatch, 10, "Failed to show color palette.");
+    }
+
+    private void clickColorPickerPalette(int yFactor) throws Exception {
+        onActionLatch = new CountDownLatch(1);
+        mouseClick(colorPicker.getLayoutX() + colorPicker.getWidth() / 2,
+                    colorPicker.getLayoutY() + colorPicker.getHeight() * yFactor);
+        Thread.sleep(400);
+        waitForLatch(onActionLatch, 10, "Failed to receive onAction callback.");
+    }
+
+    // This test is for verifying a specific behavior.
+    // 1. Remove ColorPicker, call ColorPicker.show() & add back ColorPicker.
+    // 2. Verify that the ColorPicker palette is shown using onShown,
+    // 2.1 Confirm that color palette is shown, using listener onAction.
+    // 3. Click on ColorPicker, color palette should get shown.
+    // 4. Repeat step 2.
+    // 4.1 Repeat step 2.1
+    // 5. Click on ColorPicker, color palette should get shown.
+    // 6. Remove & add back ColorPicker, color palette should get shown.
+    // 7. Confirm that color palette is shown, using listener onAction.
+
+    @Test
+    public void testColorPickerSceneChange() throws Exception {
+        Thread.sleep(1000); // Wait for stage to layout
+
+        // 1.
+        onShownLatch = new CountDownLatch(1);
+        Util.runAndWait(() -> {
+            root.getChildren().clear();
+            colorPicker.show();
+            root.getChildren().add(colorPicker);
+        });
+        waitForLatch(onShownLatch, 10, "Failed to show color palette.");
+        Thread.sleep(400); // ColorPicker takes some time to display the color palette.
+        // 2.
+        Assert.assertEquals("ColorPicker palette should be shown once.", 1, onShownCount);
+
+        // 2.1
+        clickColorPickerPalette(5);
+        Assert.assertEquals("ColorPicker palette should be clicked once.", 1, onActionCount);
+
+        // 3.
+        showColorPickerPalette();
+        // 4.
+        Assert.assertEquals("ColorPicker palette should have been shown two times.", 2, onShownCount);
+
+        // 4.1
+        clickColorPickerPalette(6);
+        Assert.assertEquals("ColorPicker palette have been clicked two times.", 2, onActionCount);
+
+        // 5.
+        showColorPickerPalette();
+        Assert.assertEquals("ColorPicker palette should have been shown three times.", 3, onShownCount);
+
+        // 6.
+        Util.runAndWait(() -> {
+            root.getChildren().clear();
+            root.getChildren().add(colorPicker);
+        });
+        Thread.sleep(400); // ColorPicker takes some time to display the color palette.
+
+        // 7.
+        clickColorPickerPalette(5);
+        Assert.assertEquals("ColorPicker palette should have been clicked three times.", 3, onActionCount);
+    }
+
+    @After
+    public void resetUI() {
+        Platform.runLater(() -> {
+            colorPicker.setOnShown(null);
+            colorPicker.setOnAction(null);
+            root.getChildren().clear();
+        });
+    }
+
+    @Before
+    public void setupUI() {
+        Platform.runLater(() -> {
+            colorPicker = new ColorPicker();
+            colorPicker.setOnShown(event -> {
+                onShownCount++;
+                onShownLatch.countDown();
+            });
+            colorPicker.setOnAction(event -> {
+                onActionCount++;
+                onActionLatch.countDown();
+            });
+            root.getChildren().add(colorPicker);
+        });
+    }
+
+    @BeforeClass
+    public static void initFX() throws Exception {
+        new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
+        waitForLatch(startupLatch, 10, "FX runtime failed to start.");
+    }
+
+    @AfterClass
+    public static void exit() {
+        Platform.runLater(() -> {
+            stage.hide();
+        });
+        Platform.exit();
+    }
+
+    public static class TestApp extends Application {
+        @Override
+        public void start(Stage primaryStage) {
+            robot = new Robot();
+            stage = primaryStage;
+            root = new VBox();
+            scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT);
+            stage.setScene(scene);
+            stage.initStyle(StageStyle.UNDECORATED);
+            stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
+                    Platform.runLater(startupLatch::countDown));
+            stage.setAlwaysOnTop(true);
+            stage.show();
+        }
+    }
+
+    public static void waitForLatch(CountDownLatch latch, int seconds, String msg) throws Exception {
+        Assert.assertTrue("Timeout: " + msg, latch.await(seconds, TimeUnit.SECONDS));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/robot/javafx/scene/ComboBoxTest.java	Fri Aug 03 17:01:47 2018 +0530
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.robot.javafx.scene;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.value.ChangeListener;
+import javafx.scene.control.ComboBox;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.VBox;
+import javafx.scene.robot.Robot;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.WindowEvent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import test.util.Util;
+
+public class ComboBoxTest {
+
+    static CountDownLatch startupLatch = new CountDownLatch(1);
+    static Robot robot;
+    static volatile Stage stage;
+    static volatile Scene scene;
+    static final int SCENE_WIDTH = 200;
+    static final int SCENE_HEIGHT = SCENE_WIDTH;
+    static VBox root;
+    int onShownCount = 0;
+    int onSelectedCount = 0;
+    final int ITEM_COUNT = 2;
+    ComboBox comboBox;
+    CountDownLatch showLatch;
+    CountDownLatch selectedLatch;
+
+    private void mouseClick(double x, double y) {
+        Util.runAndWait(() -> {
+            robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + x),
+                    (int) (scene.getWindow().getY() + scene.getY() + y));
+            robot.mousePress(MouseButton.PRIMARY);
+            robot.mouseRelease(MouseButton.PRIMARY);
+        });
+    }
+    // This test is for verifying a specific behavior.
+    // 1. Click ComboBox to show the popup list.
+    // 2. Click a choice to select.
+    // 3. Remove ComboBox and call ComboBox.show() and add it back to scene when a choice is selected.
+    // 4. Verify that the ComboBox functions correctly after adding back.
+    @Test
+    public void testComboBoxSceneChange1() throws Exception {
+        Thread.sleep(1000); // Wait for stage to layout
+        comboBox.setOnShown(event -> {
+            onShownCount++;
+            showLatch.countDown();
+        });
+        ChangeListener chListener = (observable, oldValue, newValue) -> {
+            onSelectedCount++;
+            root.getChildren().clear();
+            comboBox.show();
+            // Called ITEM_COUNT times.
+            root.getChildren().add(comboBox);
+            selectedLatch.countDown();
+        };
+        comboBox.getSelectionModel().selectedItemProperty().addListener(chListener);
+        showLatch = new CountDownLatch(1);
+
+        // Show ComboBox popup list. popup list shown once by mouse click. (ITEM_COUNT + 1)
+        mouseClick(comboBox.getLayoutX() + comboBox.getWidth() / 2,
+                    comboBox.getLayoutY() + comboBox.getHeight() / 2);
+
+        for (int i = 0; i < ITEM_COUNT; i++) {
+            Thread.sleep(300); // ComboBox is removed and added back. Time for layout.
+            waitForLatch(showLatch, 10, "Failed to show ComboBox popup list. " + i);
+            showLatch = new CountDownLatch(1);
+            selectedLatch = new CountDownLatch(1);
+            final int k = i;
+            // Select a choice.
+            mouseClick(comboBox.getLayoutX() + comboBox.getWidth() / 2,
+                        comboBox.getLayoutY() + comboBox.getHeight() * (k + 1.2f));
+
+            waitForLatch(selectedLatch, 10, "Failed to select " + i + "th choice.");
+        }
+        Assert.assertEquals("ComboBox popup list should have been displayed " +
+            (ITEM_COUNT + 1) + " times.", (ITEM_COUNT + 1), onShownCount);
+        Assert.assertEquals("ComboBox choice should have been selected " +
+            ITEM_COUNT + " times.", ITEM_COUNT, onSelectedCount);
+    }
+
+    // This test is for verifying a specific behavior.
+    // 1. Click ComboBox to show the popup list.
+    // 2. Click a choice to select.
+    // 3. Remove ComboBox and add it back to scene when a choice is selected.
+    // 4. Verify that the ComboBox functions correctly after adding back.
+    @Test
+    public void testComboBoxSceneChange2() throws Exception {
+        Thread.sleep(1000); // Wait for stage to layout
+        comboBox.setOnShown(event -> {
+            onShownCount++;
+            showLatch.countDown();
+        });
+        ChangeListener chListener = (observable, oldValue, newValue) -> {
+            onSelectedCount++;
+            root.getChildren().clear();
+            root.getChildren().add(comboBox);
+            selectedLatch.countDown();
+        };
+        comboBox.getSelectionModel().selectedItemProperty().addListener(chListener);
+        for (int i = 0; i < ITEM_COUNT; i++) {
+            Thread.sleep(300); // ComboBox is removed and added back. Time for layout.
+            showLatch = new CountDownLatch(1);
+            selectedLatch = new CountDownLatch(1);
+
+            // Show ComboBox popup list.
+            mouseClick(comboBox.getLayoutX() + comboBox.getWidth() / 2,
+                    comboBox.getLayoutY() + comboBox.getHeight() / 2);
+
+            Thread.sleep(200); // ComboBox takes some time to display the popup list.
+            waitForLatch(showLatch, 10, "Failed to show ComboBox popup list. " + i);
+            final int k = i;
+            // Select a choice.
+            mouseClick(comboBox.getLayoutX() + comboBox.getWidth() / 2,
+                        comboBox.getLayoutY() + comboBox.getHeight() * (k + 1.2f));
+
+            waitForLatch(selectedLatch, 10, "Failed to select " + i + "th choice.");
+        }
+        Assert.assertEquals("ComboBox popup list should be displayed " +
+            ITEM_COUNT + " times.", ITEM_COUNT, onShownCount);
+        Assert.assertEquals("ComboBox choice should have been selected " +
+            ITEM_COUNT + " times.", ITEM_COUNT, onSelectedCount);
+    }
+
+    @After
+    public void resetUI() {
+        Util.runAndWait(() -> {
+            root.getChildren().clear();
+        });
+    }
+
+    @Before
+    public void setupUI() {
+        Util.runAndWait(() -> {
+            comboBox = new ComboBox();
+            for (int i = 0; i < ITEM_COUNT; i++) {
+                comboBox.getItems().add("Op" + i);
+            }
+            root.getChildren().add(comboBox);
+            onShownCount = 0;
+            onSelectedCount = 0;
+        });
+    }
+
+    @BeforeClass
+    public static void initFX() throws Exception {
+        new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
+        waitForLatch(startupLatch, 10, "FX runtime failed to start.");
+    }
+
+    @AfterClass
+    public static void exit() {
+        Util.runAndWait(() -> {
+            stage.hide();
+        });
+        Platform.exit();
+    }
+
+    public static class TestApp extends Application {
+        @Override
+        public void start(Stage primaryStage) {
+            robot = new Robot();
+            stage = primaryStage;
+            root = new VBox();
+            scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT);
+            stage.setScene(scene);
+            stage.initStyle(StageStyle.UNDECORATED);
+            stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
+                    Platform.runLater(startupLatch::countDown));
+            stage.setAlwaysOnTop(true);
+            stage.show();
+        }
+    }
+
+    public static void waitForLatch(CountDownLatch latch, int seconds, String msg) throws Exception {
+        Assert.assertTrue("Timeout: " + msg, latch.await(seconds, TimeUnit.SECONDS));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/test/java/test/robot/javafx/scene/DatePickerTest.java	Fri Aug 03 17:01:47 2018 +0530
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.robot.javafx.scene;
+
+import com.sun.javafx.PlatformUtil;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.control.DatePicker;
+import javafx.scene.input.MouseButton;
+import javafx.scene.layout.VBox;
+import javafx.scene.robot.Robot;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.WindowEvent;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import test.util.Util;
+
+public class DatePickerTest {
+
+    static CountDownLatch startupLatch = new CountDownLatch(1);
+    static Robot robot;
+    static volatile Stage stage;
+    static volatile Scene scene;
+    static final int SCENE_WIDTH = 250;
+    static final int SCENE_HEIGHT = SCENE_WIDTH;
+    static VBox root;
+    int onShownCount = 0;
+    int onActionCount = 0;
+    DatePicker datePicker;
+    CountDownLatch onShownLatch;
+    CountDownLatch onActionLatch;
+
+    private void mouseClick(double x, double y) {
+        Util.runAndWait(() -> {
+            robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + x),
+                    (int) (scene.getWindow().getY() + scene.getY() + y));
+            robot.mousePress(MouseButton.PRIMARY);
+            robot.mouseRelease(MouseButton.PRIMARY);
+        });
+    }
+
+    private void showDatePickerCalendarPopup() throws Exception {
+        onShownLatch = new CountDownLatch(1);
+        mouseClick(datePicker.getLayoutX() + datePicker.getWidth() - 15,
+                    datePicker.getLayoutY() + datePicker.getHeight() / 2);
+        Thread.sleep(400); // DatePicker takes some time to display the calendar popup.
+        waitForLatch(onShownLatch, 10, "Failed to show Calendar popup.");
+    }
+
+    private void clickDatePickerCalendarPopup(int yFactor) throws Exception {
+        onActionLatch = new CountDownLatch(1);
+        mouseClick(datePicker.getLayoutX() + datePicker.getWidth() / 2,
+                    datePicker.getLayoutY() + datePicker.getHeight() * yFactor);
+        Thread.sleep(400);
+        waitForLatch(onActionLatch, 10, "Failed to receive onAction call.");
+    }
+
+    // This test is for verifying a specific behavior.
+    // 1. Remove DatePicker, call DatePicker.show() & add back DatePicker.
+    // 2. Verify that the DatePicker calendar popup is shown using onShown,
+    // 2.1 Confirm that calendar popup is shown, using listener onAction.
+    // 3. Click on DatePicker, calendar popup should get shown.
+    // 4. Repeat step 2.
+    // 4.1 Repeat step 2.1
+    // 5. Click on DatePicker, calendar popup should get shown.
+    // 6. Remove & add back DatePicker, calendar popup should get shown.
+    // 7. Confirm that calendar popup is shown, using listener onAction.
+
+    @Test
+    public void testDatePickerSceneChange() throws Exception {
+        // Disable on mac untill JDK-8208523 is fixed.
+        assumeTrue(!PlatformUtil.isMac());
+        Thread.sleep(1000); // Wait for stage to layout
+
+        // 1.
+        onShownLatch = new CountDownLatch(1);
+        Util.runAndWait(() -> {
+            root.getChildren().clear();
+            datePicker.show();
+            root.getChildren().add(datePicker);
+        });
+        waitForLatch(onShownLatch, 10, "Failed to show calendar popup.");
+        Thread.sleep(400); // DatePicker takes some time to display the calendar popup.
+        // 2.
+        Assert.assertEquals("DatePicker calendar popup should be shown once.", 1, onShownCount);
+
+        // 2.1
+        clickDatePickerCalendarPopup(5);
+        Assert.assertEquals("DatePicker calendar popup should be clicked once.", 1, onActionCount);
+
+        // 3.
+        showDatePickerCalendarPopup();
+        // 4.
+        Assert.assertEquals("DatePicker calendar popup should have been shown two times.", 2, onShownCount);
+
+        // 4.1
+        clickDatePickerCalendarPopup(6);
+        Assert.assertEquals("DatePicker calendar popup have been clicked two times.", 2, onActionCount);
+
+        // 5.
+        showDatePickerCalendarPopup();
+        Assert.assertEquals("DatePicker calendar popup should have been shown three times.", 3, onShownCount);
+
+        // 6.
+        Util.runAndWait(() -> {
+            root.getChildren().clear();
+            root.getChildren().add(datePicker);
+        });
+        Thread.sleep(400); // DatePicker takes some time to display the calendar popup.
+
+        // 7.
+        clickDatePickerCalendarPopup(5);
+        Assert.assertEquals("DatePicker calendar popup should have been clicked three times.", 3, onActionCount);
+    }
+
+    @After
+    public void resetUI() {
+        Platform.runLater(() -> {
+            datePicker.setOnShown(null);
+            datePicker.setOnAction(null);
+            root.getChildren().clear();
+        });
+    }
+
+    @Before
+    public void setupUI() {
+        Platform.runLater(() -> {
+            datePicker = new DatePicker();
+            datePicker.setOnShown(event -> {
+                onShownCount++;
+                onShownLatch.countDown();
+            });
+            datePicker.setOnAction(event -> {
+                onActionCount++;
+                onActionLatch.countDown();
+            });
+            root.getChildren().add(datePicker);
+        });
+    }
+
+    @BeforeClass
+    public static void initFX() throws Exception {
+        new Thread(() -> Application.launch(TestApp.class, (String[])null)).start();
+        waitForLatch(startupLatch, 10, "FX runtime failed to start.");
+    }
+
+    @AfterClass
+    public static void exit() {
+        Platform.runLater(() -> {
+            stage.hide();
+        });
+        Platform.exit();
+    }
+
+    public static class TestApp extends Application {
+        @Override
+        public void start(Stage primaryStage) {
+            robot = new Robot();
+            stage = primaryStage;
+            root = new VBox();
+            scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT);
+            stage.setScene(scene);
+            stage.initStyle(StageStyle.UNDECORATED);
+            stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e ->
+                    Platform.runLater(startupLatch::countDown));
+            stage.setAlwaysOnTop(true);
+            stage.show();
+        }
+    }
+
+    public static void waitForLatch(CountDownLatch latch, int seconds, String msg) throws Exception {
+        Assert.assertTrue("Timeout: " + msg, latch.await(seconds, TimeUnit.SECONDS));
+    }
+}