1 /* 2 * Copyright (c) 2013, 2015, 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 java.time.LocalDate; 29 import java.time.chrono.*; 30 import java.util.*; 31 32 import com.sun.javafx.scene.control.infrastructure.KeyEventFirer; 33 import com.sun.javafx.scene.control.infrastructure.KeyModifier; 34 import com.sun.javafx.scene.control.infrastructure.StageLoader; 35 import com.sun.javafx.tk.Toolkit; 36 import javafx.beans.property.ObjectProperty; 37 import javafx.beans.property.SimpleObjectProperty; 38 import javafx.beans.property.SimpleStringProperty; 39 import javafx.beans.property.StringProperty; 40 import javafx.event.ActionEvent; 41 import javafx.event.EventHandler; 42 import javafx.scene.Node; 43 import javafx.scene.Scene; 44 import javafx.scene.input.KeyCode; 45 import javafx.scene.layout.VBox; 46 import javafx.util.Callback; 47 import javafx.util.StringConverter; 48 49 import static com.sun.javafx.scene.control.infrastructure.ControlTestUtils.assertStyleClassContains; 50 51 import javafx.scene.control.skin.DatePickerSkin; 52 53 import org.junit.Before; 54 import org.junit.Test; 55 56 import static org.junit.Assert.*; 57 import static org.junit.Assert.assertEquals; 58 59 public class DatePickerTest { 60 private DatePicker datePicker; 61 private final LocalDate today = LocalDate.now(); 62 63 64 /********************************************************************* 65 * * 66 * Utility methods * 67 * * 68 ********************************************************************/ 69 70 public Node getDisplayNode() { 71 return ((DatePickerSkin)datePicker.getSkin()).getDisplayNode(); 72 } 73 74 75 76 /********************************************************************* 77 * * 78 * Setup * 79 * * 80 ********************************************************************/ 81 82 @Before public void setup() { 83 Locale.setDefault(Locale.forLanguageTag("en-US")); 84 datePicker = new DatePicker(); 85 } 86 87 88 89 /********************************************************************* 90 * * 91 * Tests for the constructors * 92 * * 93 ********************************************************************/ 94 95 @Test public void noArgConstructorSetsTheStyleClass() { 96 assertStyleClassContains(datePicker, "date-picker"); 97 } 98 99 @Test public void noArgConstructor_valueIsNull() { 100 assertNull(datePicker.getValue()); 101 } 102 103 @Test public void noArgConstructor_editableIsTrue() { 104 assertTrue(datePicker.isEditable()); 105 } 106 107 @Test public void noArgConstructor_showingIsFalse() { 108 assertFalse(datePicker.isShowing()); 109 } 110 111 @Test public void noArgConstructor_promptTextIsEmptyString() { 112 assertEquals("", datePicker.getPromptText()); 113 } 114 115 @Test public void noArgConstructor_armedIsFalse() { 116 assertFalse(datePicker.isArmed()); 117 } 118 119 @Test public void noArgConstructor_converterIsNotNull() { 120 assertNotNull(datePicker.getConverter()); 121 } 122 123 @Test public void noArgConstructor_chronologyIsNotNull() { 124 assertNotNull(datePicker.getChronology()); 125 } 126 127 @Test public void noArgConstructor_dayCellFactoryIsNull() { 128 assertNull(datePicker.getDayCellFactory()); 129 } 130 131 @Test public void singleArgConstructorSetsTheStyleClass() { 132 final DatePicker b2 = new DatePicker(today); 133 assertStyleClassContains(b2, "date-picker"); 134 } 135 136 @Test public void singleArgConstructor_valueIsArg() { 137 final DatePicker b2 = new DatePicker(today); 138 assertEquals(b2.getValue(), today); 139 } 140 141 @Test public void singleArgConstructor_editableIsTrue() { 142 final DatePicker b2 = new DatePicker(today); 143 assertTrue(b2.isEditable()); 144 } 145 146 @Test public void singleArgConstructor_showingIsFalse() { 147 final DatePicker b2 = new DatePicker(today); 148 assertFalse(b2.isShowing()); 149 } 150 151 @Test public void singleArgConstructor_promptTextIsEmptyString() { 152 final DatePicker b2 = new DatePicker(today); 153 assertEquals("", b2.getPromptText()); 154 } 155 156 @Test public void singleArgConstructor_armedIsFalse() { 157 final DatePicker b2 = new DatePicker(today); 158 assertEquals(false, b2.isArmed()); 159 } 160 161 @Test public void singleArgConstructor_converterIsNotNull() { 162 final DatePicker b2 = new DatePicker(today); 163 assertNotNull(b2.getConverter()); 164 } 165 166 @Test public void singleArgConstructor_chronologyIsNotNull() { 167 final DatePicker b2 = new DatePicker(today); 168 assertNotNull(b2.getChronology()); 169 } 170 171 @Test public void singleArgConstructor_dayCellFactoryIsNull() { 172 final DatePicker b2 = new DatePicker(today); 173 assertNull(b2.getDayCellFactory()); 174 } 175 176 177 /********************************************************************* 178 * Tests for default values * 179 ********************************************************************/ 180 181 @Test public void checkPromptTextPropertyName() { 182 assertTrue(datePicker.promptTextProperty().getName().equals("promptText")); 183 } 184 185 @Test public void checkValuePropertyName() { 186 assertTrue(datePicker.valueProperty().getName().equals("value")); 187 } 188 189 @Test public void checkConverterPropertyName() { 190 assertTrue(datePicker.converterProperty().getName().equals("converter")); 191 } 192 193 @Test public void checkChronologyPropertyName() { 194 assertTrue(datePicker.chronologyProperty().getName().equals("chronology")); 195 } 196 197 @Test public void checkOnActionPropertyName() { 198 assertTrue(datePicker.onActionProperty().getName().equals("onAction")); 199 } 200 201 @Test public void checkArmedPropertyName() { 202 assertTrue(datePicker.armedProperty().getName().equals("armed")); 203 } 204 205 @Test public void checkShowingPropertyName() { 206 assertTrue(datePicker.showingProperty().getName().equals("showing")); 207 } 208 209 @Test public void checkEditablePropertyName() { 210 assertTrue(datePicker.editableProperty().getName().equals("editable")); 211 } 212 213 @Test public void checkDayCellFactoryPropertyName() { 214 assertTrue(datePicker.dayCellFactoryProperty().getName().equals("dayCellFactory")); 215 } 216 217 @Test public void defaultActionHandlerIsNotDefined() { 218 assertNull(datePicker.getOnAction()); 219 } 220 221 @Test public void defaultConverterCanHandleLocalDateValues() { 222 StringConverter<LocalDate> sc = datePicker.getConverter(); 223 String todayStr = sc.toString(today); 224 assertTrue(todayStr.length() > 0); 225 assertEquals(today, sc.fromString(todayStr)); 226 } 227 228 @Test public void defaultConverterCanHandleNullValues() { 229 StringConverter<LocalDate> sc = datePicker.getConverter(); 230 String str = sc.toString(null); 231 232 assertEquals(null, sc.fromString(null)); 233 assertEquals(null, sc.fromString("")); 234 assertTrue(str == null || str.isEmpty()); 235 } 236 237 238 /********************************************************************* 239 * Tests for properties * 240 ********************************************************************/ 241 242 @Test public void ensureSettingNullChronologyRestoresDefault() { 243 Chronology defaultChronology = datePicker.getChronology(); 244 Chronology otherChronology = 245 (defaultChronology != IsoChronology.INSTANCE) ? IsoChronology.INSTANCE : JapaneseChronology.INSTANCE; 246 datePicker.setChronology(otherChronology); 247 assertEquals(otherChronology, datePicker.getChronology()); 248 datePicker.setChronology(null); 249 assertEquals(defaultChronology, datePicker.getChronology()); 250 } 251 252 @Test public void ensureSettingNullConverterRestoresDefault() { 253 StringConverter<LocalDate> defaultConverter = datePicker.getConverter(); 254 datePicker.setConverter(new StringConverter<LocalDate>() { 255 @Override public String toString(LocalDate t) { return t.toString(); } 256 @Override public LocalDate fromString(String string) { return today; } 257 }); 258 assertNotSame(defaultConverter, datePicker.getConverter()); 259 datePicker.setConverter(null); 260 assertSame(defaultConverter, datePicker.getConverter()); 261 } 262 263 @Test public void ensureCanSetNonNullDayCellFactory() { 264 Callback<DatePicker, DateCell> cf = p -> null; 265 datePicker.setDayCellFactory(cf); 266 assertSame(cf, datePicker.getDayCellFactory()); 267 } 268 269 @Test public void ensureEditorIsNonNullWhenComboBoxIsNotEditable() { 270 assertNotNull(datePicker.getEditor()); 271 } 272 273 @Test public void ensureEditorIsNonNullWhenComboBoxIsEditable() { 274 datePicker.setEditable(true); 275 assertNotNull(datePicker.getEditor()); 276 } 277 278 @Test public void ensureEditorDoesNotChangeWhenEditableToggles() { 279 datePicker.setEditable(true); 280 assertNotNull(datePicker.getEditor()); 281 datePicker.setEditable(false); 282 assertNotNull(datePicker.getEditor()); 283 datePicker.setEditable(true); 284 assertNotNull(datePicker.getEditor()); 285 } 286 287 @Test public void ensureCanSetValueToNonNullLocalDateAndBackAgain() { 288 datePicker.setValue(today); 289 assertEquals(today, datePicker.getValue()); 290 datePicker.setValue(null); 291 assertNull(datePicker.getValue()); 292 } 293 294 @Test public void ensureCanToggleEditable() { 295 datePicker.setEditable(true); 296 assertTrue(datePicker.isEditable()); 297 datePicker.setEditable(false); 298 assertFalse(datePicker.isEditable()); 299 } 300 301 @Test public void ensureCanToggleShowing() { 302 datePicker.show(); 303 assertTrue(datePicker.isShowing()); 304 datePicker.hide(); 305 assertFalse(datePicker.isShowing()); 306 } 307 308 @Test public void ensureCanNotToggleShowingWhenDisabled() { 309 datePicker.setDisable(true); 310 datePicker.show(); 311 assertFalse(datePicker.isShowing()); 312 datePicker.setDisable(false); 313 datePicker.show(); 314 assertTrue(datePicker.isShowing()); 315 } 316 317 @Test public void ensureCanSetPromptText() { 318 datePicker.setPromptText("Test 1 2 3"); 319 assertEquals("Test 1 2 3", datePicker.getPromptText()); 320 } 321 322 @Test public void ensureCanSetPromptTextToNull() { 323 assertEquals("", datePicker.getPromptText()); 324 datePicker.setPromptText(null); 325 assertEquals(null, datePicker.getPromptText()); 326 } 327 328 @Test public void ensurePromptTextStripsNewlines() { 329 datePicker.setPromptText("Test\n1\n2\n3"); 330 assertEquals("Test123", datePicker.getPromptText()); 331 } 332 333 @Test public void ensureCanToggleArmed() { 334 assertFalse(datePicker.isArmed()); 335 datePicker.arm(); 336 assertTrue(datePicker.isArmed()); 337 datePicker.disarm(); 338 assertFalse(datePicker.isArmed()); 339 } 340 341 @Test public void ensureCanSetOnAction() { 342 EventHandler<ActionEvent> onAction = t -> { }; 343 datePicker.setOnAction(onAction); 344 assertEquals(onAction, datePicker.getOnAction()); 345 } 346 347 @Test public void ensureOnActionPropertyReferencesBean() { 348 assertEquals(datePicker, datePicker.onActionProperty().getBean()); 349 } 350 351 /********************************************************************* 352 * Tests for property binding * 353 ********************************************************************/ 354 @Test public void checkPromptTextPropertyBind() { 355 StringProperty strPr = new SimpleStringProperty("value"); 356 datePicker.promptTextProperty().bind(strPr); 357 assertTrue("PromptText cannot be bound", datePicker.getPromptText().equals("value")); 358 strPr.setValue("newvalue"); 359 assertTrue("PromptText cannot be bound", datePicker.getPromptText().equals("newvalue")); 360 } 361 362 @Test public void checkValuePropertyBind() { 363 ObjectProperty<LocalDate> objPr = new SimpleObjectProperty<LocalDate>(today); 364 datePicker.valueProperty().bind(objPr); 365 assertTrue("value cannot be bound", datePicker.getValue().equals(today)); 366 LocalDate tomorrow = today.plusDays(1); 367 objPr.setValue(tomorrow); 368 assertTrue("value cannot be bound", datePicker.getValue().equals(tomorrow)); 369 } 370 371 @Test public void checkChronologyPropertyBind() { 372 ObjectProperty<Chronology> objPr = new SimpleObjectProperty<Chronology>(IsoChronology.INSTANCE); 373 datePicker.chronologyProperty().bind(objPr); 374 assertTrue("Chronology cannot be bound", datePicker.getChronology().equals(IsoChronology.INSTANCE)); 375 objPr.setValue(JapaneseChronology.INSTANCE); 376 assertTrue("Chronology cannot be bound", datePicker.getChronology().equals(JapaneseChronology.INSTANCE)); 377 } 378 379 380 /********************************************************************* 381 * Tests for bug reports * 382 ********************************************************************/ 383 384 @Test public void test_rt21186() { 385 final DatePicker datePicker = new DatePicker(); 386 387 StageLoader sl = new StageLoader(datePicker); 388 389 assertNull(datePicker.getTooltip()); 390 assertNull(datePicker.getEditor().getTooltip()); 391 392 Tooltip tooltip = new Tooltip("Tooltip"); 393 datePicker.setTooltip(tooltip); 394 assertEquals(tooltip, datePicker.getTooltip()); 395 assertEquals(tooltip, datePicker.getEditor().getTooltip()); 396 397 datePicker.setTooltip(null); 398 assertNull(datePicker.getTooltip()); 399 assertNull(datePicker.getEditor().getTooltip()); 400 401 sl.dispose(); 402 } 403 404 @Test public void test_rt30549() { 405 // Set a MinguoDate from a String 406 datePicker.setChronology(MinguoChronology.INSTANCE); 407 datePicker.getEditor().setText("5/22/0102 1"); 408 datePicker.setValue(datePicker.getConverter().fromString(datePicker.getEditor().getText())); 409 assertEquals(MinguoChronology.INSTANCE.date(MinguoEra.ROC, 102, 5, 22), 410 MinguoDate.from(datePicker.getValue())); 411 assertEquals("5/22/0102 1", datePicker.getEditor().getText()); 412 413 // Convert from MinguoDate to LocalDate (ISO) 414 datePicker.setChronology(IsoChronology.INSTANCE); 415 assertEquals(LocalDate.of(2013, 5, 22), datePicker.getValue()); 416 datePicker.getEditor().setText(datePicker.getConverter().toString(datePicker.getValue())); 417 assertEquals("5/22/2013", datePicker.getEditor().getText()); 418 } 419 420 private int test_rt35586_count = 0; 421 @Test public void test_rt35586() { 422 assertEquals(0, test_rt35586_count); 423 424 final DatePicker dp = new DatePicker(); 425 dp.setOnAction(event -> { 426 test_rt35586_count++; 427 assertEquals("1/2/2015", dp.getEditor().getText()); 428 }); 429 430 StageLoader sl = new StageLoader(dp); 431 432 dp.requestFocus(); 433 dp.getEditor().setText("1/2/2015"); 434 KeyEventFirer keyboard = new KeyEventFirer(dp); 435 keyboard.doKeyPress(KeyCode.ENTER); 436 437 assertEquals(1, test_rt35586_count); 438 439 sl.dispose(); 440 } 441 442 @Test public void test_rt35840() { 443 final DatePicker dp = new DatePicker(); 444 dp.setEditable(true); 445 StageLoader sl = new StageLoader(dp); 446 dp.requestFocus(); 447 448 KeyEventFirer keyboard = new KeyEventFirer(dp); 449 keyboard.doKeyTyped(KeyCode.DIGIT1); 450 keyboard.doKeyTyped(KeyCode.SLASH); 451 keyboard.doKeyTyped(KeyCode.DIGIT2); 452 keyboard.doKeyTyped(KeyCode.SLASH); 453 keyboard.doKeyTyped(KeyCode.DIGIT2); 454 keyboard.doKeyTyped(KeyCode.DIGIT0); 455 keyboard.doKeyTyped(KeyCode.DIGIT1); 456 keyboard.doKeyTyped(KeyCode.DIGIT5); 457 assertEquals("1/2/2015", dp.getEditor().getText()); 458 459 assertNull(dp.getValue()); 460 keyboard.doKeyPress(KeyCode.ENTER); 461 assertEquals("2015-01-02", dp.getValue().toString()); 462 463 sl.dispose(); 464 } 465 466 @Test public void test_rt36280_F4ShowsPopup() { 467 final DatePicker dp = new DatePicker(); 468 StageLoader sl = new StageLoader(dp); 469 KeyEventFirer dpKeyboard = new KeyEventFirer(dp); 470 471 assertFalse(dp.isShowing()); 472 dpKeyboard.doKeyPress(KeyCode.F4); // show the popup 473 assertTrue(dp.isShowing()); 474 475 sl.dispose(); 476 } 477 478 @Test public void test_rt36280_altUpShowsPopup() { 479 final DatePicker dp = new DatePicker(); 480 StageLoader sl = new StageLoader(dp); 481 KeyEventFirer dpKeyboard = new KeyEventFirer(dp); 482 483 assertFalse(dp.isShowing()); 484 dpKeyboard.doKeyPress(KeyCode.UP, KeyModifier.ALT); // show the popup 485 assertTrue(dp.isShowing()); 486 487 sl.dispose(); 488 } 489 490 @Test public void test_rt36280_altDownShowsPopup_onComboBox() { 491 final DatePicker dp = new DatePicker(); 492 StageLoader sl = new StageLoader(dp); 493 KeyEventFirer dpKeyboard = new KeyEventFirer(dp); 494 495 assertFalse(dp.isShowing()); 496 assertTrue(dp.getEditor().getText().isEmpty()); 497 dpKeyboard.doKeyPress(KeyCode.DOWN, KeyModifier.ALT); // show the popup 498 assertTrue(dp.isShowing()); 499 assertTrue(dp.getEditor().getText().isEmpty()); 500 501 sl.dispose(); 502 } 503 504 @Test public void test_rt36280_altDownShowsPopup_onTextField() { 505 final DatePicker dp = new DatePicker(); 506 StageLoader sl = new StageLoader(dp); 507 508 KeyEventFirer tfKeyboard = new KeyEventFirer(dp.getEditor()); 509 assertFalse(dp.isShowing()); 510 assertTrue(dp.getEditor().getText().isEmpty()); 511 tfKeyboard.doKeyPress(KeyCode.DOWN, KeyModifier.ALT); // show the popup 512 assertTrue(dp.isShowing()); 513 assertTrue(dp.getEditor().getText().isEmpty()); 514 515 sl.dispose(); 516 } 517 518 @Test public void test_rt36280_F4HidesShowingPopup() { 519 final DatePicker dp = new DatePicker(); 520 StageLoader sl = new StageLoader(dp); 521 KeyEventFirer dpKeyboard = new KeyEventFirer(dp); 522 523 assertFalse(dp.isShowing()); 524 dpKeyboard.doKeyPress(KeyCode.F4); // show the popup 525 assertTrue(dp.isShowing()); 526 dpKeyboard.doKeyPress(KeyCode.F4); // hide the popup 527 assertFalse(dp.isShowing()); 528 529 sl.dispose(); 530 } 531 532 @Test public void test_rt36717() { 533 final DatePicker dp = new DatePicker(); 534 StageLoader sl = new StageLoader(dp); 535 536 // the stack overflow only occurs when a ComboBox changes from non-editable to editable 537 dp.setEditable(false); 538 dp.setEditable(true); 539 assertNotNull(dp.getEditor()); 540 KeyEventFirer tfKeyboard = new KeyEventFirer(dp.getEditor()); 541 tfKeyboard.doKeyPress(KeyCode.ENTER); // Stack overflow here 542 543 sl.dispose(); 544 } 545 546 @Test public void test_rt36902() { 547 final DatePicker dp1 = new DatePicker() { 548 @Override public String toString() { 549 return "dp1"; 550 } 551 }; 552 final DatePicker dp2 = new DatePicker() { 553 @Override public String toString() { 554 return "dp2"; 555 } 556 }; 557 dp2.setEditable(true); 558 VBox vbox = new VBox(dp1, dp2); 559 560 // lame - I would rather have one keyboard here but I couldn't get it to 561 // work, so watch out for which keyboard is used below 562 KeyEventFirer dp1Keyboard = new KeyEventFirer(dp1); 563 KeyEventFirer dp2Keyboard = new KeyEventFirer(dp2); 564 565 StageLoader sl = new StageLoader(vbox); 566 sl.getStage().requestFocus(); 567 dp1.requestFocus(); 568 Toolkit.getToolkit().firePulse(); 569 Scene scene = sl.getStage().getScene(); 570 571 assertTrue(dp1.isFocused()); 572 assertEquals(dp1, scene.getFocusOwner()); 573 574 // move focus forward to dp2 575 dp1Keyboard.doKeyPress(KeyCode.TAB); 576 assertTrue(dp2.isFocused()); 577 assertEquals(dp2, scene.getFocusOwner()); 578 579 // move focus forward again to dp1 580 dp2Keyboard.doKeyPress(KeyCode.TAB); 581 assertTrue(dp1.isFocused()); 582 assertEquals(dp1, scene.getFocusOwner()); 583 584 // now start going backwards with shift-tab. 585 // The first half of the bug is here - when we shift-tab into dp2, we 586 // actually go into the FakeFocusTextField subcomponent, so whilst the 587 // dp2.isFocused() returns true as expected, the scene focus owner is 588 // not the ComboBox, but the FakeFocusTextField inside it 589 dp1Keyboard.doKeyPress(KeyCode.TAB, KeyModifier.SHIFT); 590 assertTrue("Expect dp2 to be focused, but actual focus owner is: " + scene.getFocusOwner(), 591 dp2.isFocused()); 592 // Updated with fix for RT-34602: The TextField now never gets 593 // focus (it's just faking it). 594 // assertEquals("Expect dp2 TextField to be focused, but actual focus owner is: " + scene.getFocusOwner(), 595 // dp2.getEditor(), scene.getFocusOwner()); 596 assertEquals("Expect dp2 to be focused, but actual focus owner is: " + scene.getFocusOwner(), 597 dp2, scene.getFocusOwner()); 598 599 // This is where the second half of the bug appears, as we are stuck in 600 // the FakeFocusTextField of dp2, we never make it to dp1 601 dp2Keyboard.doKeyPress(KeyCode.TAB, KeyModifier.SHIFT); 602 assertTrue(dp1.isFocused()); 603 assertEquals(dp1, scene.getFocusOwner()); 604 605 sl.dispose(); 606 } 607 }