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