1 /* 2 * Copyright (c) 2013, 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 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_rt30549() { 388 // Set a MinguoDate from a String 389 datePicker.setChronology(MinguoChronology.INSTANCE); 390 datePicker.getEditor().setText("5/22/0102 1"); 391 datePicker.setValue(datePicker.getConverter().fromString(datePicker.getEditor().getText())); 392 assertEquals(MinguoChronology.INSTANCE.date(MinguoEra.ROC, 102, 5, 22), 393 MinguoDate.from(datePicker.getValue())); 394 assertEquals("5/22/0102 1", datePicker.getEditor().getText()); 395 396 // Convert from MinguoDate to LocalDate (ISO) 397 datePicker.setChronology(IsoChronology.INSTANCE); 398 assertEquals(LocalDate.of(2013, 5, 22), datePicker.getValue()); 399 datePicker.getEditor().setText(datePicker.getConverter().toString(datePicker.getValue())); 400 assertEquals("5/22/2013", datePicker.getEditor().getText()); 401 } 402 403 @Ignore("fix not yet developed") 404 @Test public void test_rt36902() { 405 final DatePicker dp1 = new DatePicker() { 406 @Override public String toString() { 407 return "dp1"; 408 } 409 }; 410 final DatePicker dp2 = new DatePicker() { 411 @Override public String toString() { 412 return "dp2"; 413 } 414 }; 415 dp2.setEditable(true); 416 VBox vbox = new VBox(dp1, dp2); 417 418 // lame - I would rather have one keyboard here but I couldn't get it to 419 // work, so watch out for which keyboard is used below 420 KeyEventFirer dp1Keyboard = new KeyEventFirer(dp1); 421 KeyEventFirer dp2Keyboard = new KeyEventFirer(dp2); 422 423 StageLoader sl = new StageLoader(vbox); 424 sl.getStage().requestFocus(); 425 dp1.requestFocus(); 426 Toolkit.getToolkit().firePulse(); 427 Scene scene = sl.getStage().getScene(); 428 429 assertTrue(dp1.isFocused()); 430 assertEquals(dp1, scene.getFocusOwner()); 431 432 // move focus forward to dp2 433 dp1Keyboard.doKeyPress(KeyCode.TAB); 434 assertTrue(dp2.isFocused()); 435 assertEquals(dp2, scene.getFocusOwner()); 436 437 // move focus forward again to dp1 438 dp2Keyboard.doKeyPress(KeyCode.TAB); 439 assertTrue(dp1.isFocused()); 440 assertEquals(dp1, scene.getFocusOwner()); 441 442 // now start going backwards with shift-tab. 443 // The first half of the bug is here - when we shift-tab into dp2, we 444 // actually go into the FakeFocusTextField subcomponent, so whilst the 445 // dp2.isFocused() returns true as expected, the scene focus owner is 446 // not the ComboBox, but the FakeFocusTextField inside it 447 dp1Keyboard.doKeyPress(KeyCode.TAB, KeyModifier.SHIFT); 448 assertTrue("Expect dp2 to be focused, but actual focus owner is: " + scene.getFocusOwner(), 449 dp2.isFocused()); 450 assertEquals("Expect dp2 TextField to be focused, but actual focus owner is: " + scene.getFocusOwner(), 451 dp2.getEditor(), scene.getFocusOwner()); 452 453 // This is where the second half of the bug appears, as we are stuck in 454 // the FakeFocusTextField of dp2, we never make it to dp1 455 dp2Keyboard.doKeyPress(KeyCode.TAB, KeyModifier.SHIFT); 456 assertTrue(dp1.isFocused()); 457 assertEquals(dp1, scene.getFocusOwner()); 458 459 sl.dispose(); 460 } 461 }