1 /*
   2  * Copyright (c) 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 package javafx.scene.control.test.datepicker;
  26 
  27 import com.sun.javafx.scene.control.skin.LabeledText;
  28 import java.time.LocalDate;
  29 import java.time.Month;
  30 import java.time.YearMonth;
  31 import java.time.chrono.Chronology;
  32 import java.util.ArrayList;
  33 import java.util.HashMap;
  34 import java.util.List;
  35 import java.util.Locale;
  36 import java.util.Map;
  37 import java.util.logging.Level;
  38 import java.util.logging.Logger;
  39 import javafx.scene.Node;
  40 import javafx.scene.Scene;
  41 import javafx.scene.control.Button;
  42 import javafx.scene.control.DateCell;
  43 import javafx.scene.control.DatePicker;
  44 import javafx.scene.control.Label;
  45 import javafx.scene.control.TextField;
  46 import static javafx.scene.control.test.datepicker.DatePickerApp.*;
  47 import javafx.scene.control.test.util.UtilTestFunctions;
  48 import javafx.scene.layout.Pane;
  49 import javafx.scene.layout.Region;
  50 import javafx.scene.text.Text;
  51 import javafx.util.Callback;
  52 import javafx.util.StringConverter;
  53 import org.jemmy.Point;
  54 import org.jemmy.action.GetAction;
  55 import org.jemmy.control.Wrap;
  56 import org.jemmy.env.Environment;
  57 import org.jemmy.fx.ByID;
  58 import org.jemmy.fx.ByStyleClass;
  59 import org.jemmy.fx.Root;
  60 import org.jemmy.fx.control.TextInputControlDock;
  61 import org.jemmy.interfaces.Keyboard;
  62 import org.jemmy.interfaces.Mouse.MouseButtons;
  63 import org.jemmy.interfaces.Parent;
  64 import org.jemmy.lookup.Lookup;
  65 import org.jemmy.lookup.LookupCriteria;
  66 import org.jemmy.timing.State;
  67 import org.junit.After;
  68 import static org.junit.Assert.*;
  69 import org.junit.Before;
  70 import org.junit.BeforeClass;
  71 import org.junit.runner.RunWith;
  72 import test.javaclient.shared.FilteredTestRunner;
  73 import test.javaclient.shared.JemmyUtils;
  74 import static test.javaclient.shared.TestUtil.isEmbedded;
  75 import test.javaclient.shared.Utils;
  76 
  77 /**
  78  * @author Alexander Kirov
  79  */
  80 @RunWith(FilteredTestRunner.class)
  81 public class TestBase extends UtilTestFunctions {
  82 
  83     static Wrap<? extends DatePicker> testedControl;
  84     static Wrap<? extends Scene> scene;
  85     static Wrap<? extends Scene> popupScene;
  86     protected boolean resetHardByDefault = true;//switcher of hard and soft reset mode.
  87     protected boolean doNextResetHard = resetHardByDefault;
  88     static final LocalDate DEFAULT_DATE = LocalDate.of(1990, Month.OCTOBER, 11);
  89 
  90     //WIN7 : W : 249.0 H : 228.0 ; 8.0b109 ; (H-U:W:270.0;H:297.0)
  91     //LIN : TODO:
  92     //MAC : TODO:
  93     static final int POPUP_MIN_EXPECTED_WIDTH = 240;
  94     static final int POPUP_MAX_EXPECTED_WIDTH = 260;
  95     static final int POPUP_MIN_EXPECTED_HEIGHT = 220;
  96     static final int POPUP_MAX_EXPECTED_HEIGHT = 240;
  97 
  98     @Before
  99     public void setUp() {
 100         initWrappers();
 101         Environment.getEnvironment().setTimeout("wait.state", isEmbedded() ? 60000 : 2000);
 102         Environment.getEnvironment().setTimeout("wait.control", isEmbedded() ? 60000 : 1000);
 103         scene.mouse().move(new Point(0, 0));
 104         Locale.setDefault(Locale.ENGLISH);
 105     }
 106 
 107     @BeforeClass
 108     public static void setUpClass() throws Exception {
 109         JemmyUtils.setJemmyComparatorByDistance(0.01f);
 110         DatePickerApp.main(null);
 111         currentSettingOption = SettingOption.PROGRAM;
 112     }
 113 
 114     @After
 115     public void tearDown() throws InterruptedException {
 116         if (isPopupVisible()) {
 117             scene.mouse().click(1, new Point(0, 0));
 118         }
 119 
 120         if (doNextResetHard) {
 121             resetSceneHard();
 122         } else {
 123             resetSceneSoft();
 124         }
 125 
 126         doNextResetHard = resetHardByDefault;
 127     }
 128 
 129     protected void initWrappers() {
 130         scene = Root.ROOT.lookup().wrap();
 131         parent = scene.as(Parent.class, Node.class);
 132 
 133         testedControl = (Wrap<? extends DatePicker>) parent.lookup(DatePicker.class, new ByID<DatePicker>(TESTED_DATEPICKER_ID)).wrap();
 134     }
 135 
 136     //                   ACTIONS
 137     protected void resetSceneSoft() {
 138         clickButtonForTestPurpose(SOFT_RESET_BUTTON_ID);
 139     }
 140 
 141     protected void resetSceneHard() {
 142         clickButtonForTestPurpose(HARD_RESET_BUTTON_ID);
 143     }
 144 
 145     protected void doNextResetHard() {
 146         doNextResetHard = true;
 147     }
 148 
 149     protected void doNextResetSoft() {
 150         doNextResetHard = false;
 151     }
 152 
 153     protected void clickControl() {
 154         testedControl.mouse().click();
 155     }
 156 
 157     protected void clickDropDownButton() {
 158         testedControl.as(Parent.class, Node.class).lookup(Region.class, new ByStyleClass("arrow-button")).wrap().mouse().click();
 159     }
 160 
 161     protected Wrap<? extends Scene> getPopupWrap() throws InterruptedException {
 162         try {
 163             return Root.ROOT.lookup(new LookupCriteria<Scene>() {
 164                 int shift;
 165                 int widthAddition;
 166                 int heightAddition;
 167 
 168                 {
 169                     Boolean weekNumbersShowing = new GetAction<Boolean>() {
 170                         @Override
 171                         public void run(Object... parameters) throws Exception {
 172                             setResult(Boolean.valueOf(testedControl.getControl().isShowWeekNumbers()));
 173                         }
 174                     }.dispatch(testedControl.getEnvironment());
 175 
 176                     Boolean isHijrahUmalqura = new GetAction<Boolean>() {
 177                         @Override
 178                         public void run(Object... parameters) throws Exception {
 179                             setResult(Boolean.valueOf(testedControl.getControl().getChronology().equals(Chronology.of("Hijrah-umalqura"))));
 180                         }
 181                     }.dispatch(testedControl.getEnvironment());
 182 
 183                     if (weekNumbersShowing == Boolean.TRUE) {
 184                         shift = 27;
 185                     }
 186 
 187                     if (isHijrahUmalqura) {
 188                         widthAddition = 20;
 189                         heightAddition = 77;
 190                     }
 191                 }
 192 
 193                 public boolean check(Scene cntrl) {
 194                     return cntrl.lookup(".date-picker-popup") != null;
 195                 }
 196             }).wrap();
 197         } catch (Exception e) {
 198             return null;
 199         }
 200     }
 201 
 202     protected boolean isPopupVisible() throws InterruptedException {
 203         if ((popupScene = getPopupWrap()) != null) {
 204             System.out.println("Popup was found");
 205             Boolean res = new GetAction<Boolean>() {
 206                 @Override
 207                 public void run(Object... os) throws Exception {
 208                     setResult(popupScene.getControl().getWindow().showingProperty().getValue());
 209                 }
 210             }.dispatch(Root.ROOT.getEnvironment());
 211             if (res) {
 212                 System.out.println("Checking coordinates");
 213                 checkPopupCoordinates(popupScene);
 214             }
 215             return res;
 216         } else {
 217             System.out.println("Popup was not found.");
 218             return false;
 219         }
 220     }
 221 
 222     public void checkPopupVisibility(boolean expectedVisibility) throws InterruptedException {
 223         assertEquals(expectedVisibility, isPopupVisible());
 224     }
 225 
 226     protected void showPopup() throws InterruptedException {
 227         if (!isPopupVisible()) {
 228             clickDropDownButton();
 229             waitPopupShowingState(true);
 230             final Wrap<? extends Scene> popupWrap = getPopupWrap();
 231             assertNotNull(popupWrap);
 232             checkPopupCoordinates(popupWrap);
 233         }
 234     }
 235 
 236     protected void waitPopupShowingState(final boolean showing) throws InterruptedException {
 237         testedControl.waitState(new State() {
 238             public Object reached() {
 239                 if (testedControl.getControl().isShowing() == showing) {
 240                     try {
 241                         if (showing == isPopupVisible()) {
 242                             return true;
 243                         }
 244                     } catch (InterruptedException ex) {
 245                         return null;
 246                     }
 247                     return true;
 248                 } else {
 249                     return null;
 250                 }
 251             }
 252 
 253             @Override public String toString() { return String.format("[Popup menu is showing = %b]", showing); }
 254         });
 255     }
 256 
 257     /*
 258      * Checks alignment of the popup with the date field
 259      */
 260     protected void checkPopupCoordinates(final Wrap<? extends Scene> popupWrap) throws InterruptedException {
 261         System.out.println("popupWrap = " + popupWrap);
 262         System.out.println("popupWrap.as(Parent.class, Pane.class) = " + popupWrap.as(Parent.class, Pane.class));
 263         Lookup lookup = popupWrap.as(Parent.class, Pane.class).lookup(new ByStyleClass("date-picker-popup"));
 264         assertEquals("[Popup content not found]", 1, lookup.size());
 265 
 266         Wrap content = lookup.wrap();
 267 
 268         assertEquals(testedControl.getScreenBounds().x, content.getScreenBounds().x, 1);
 269         assertEquals(testedControl.getScreenBounds().y + testedControl.getScreenBounds().height, content.getScreenBounds().y, 1);
 270     }
 271 
 272     Wrap getRootWrap(final Wrap<? extends Scene> popupWrap) {
 273         return popupWrap.as(Parent.class, Node.class).lookup(new ByStyleClass("date-picker-popup")).wrap();
 274     }
 275 
 276     void selectAllText(TextInputControlDock input) {
 277         input.keyboard().pushKey(Keyboard.KeyboardButtons.A, Utils.isMacOS() ? Keyboard.KeyboardModifiers.META_DOWN_MASK : Keyboard.KeyboardModifiers.CTRL_DOWN_MASK);
 278     }
 279 
 280     public class DateCellDescription {
 281 
 282         public final int mainDate;
 283         public final int secondaryDate;
 284 
 285         public DateCellDescription(Wrap<? extends DateCell> dateCellWrap) {
 286             Lookup lookup = dateCellWrap.as(Parent.class, Node.class).lookup();
 287             if (lookup.lookup(LabeledText.class).size() > 0) {
 288                 final Wrap<? extends LabeledText> mainText = lookup.lookup(LabeledText.class).wrap();
 289                 mainDate = Integer.parseInt(getText(mainText));
 290             } else {
 291                 mainDate = -1;
 292             }
 293 
 294             if (lookup.lookup(Text.class, new ByStyleClass("secondary-text")).size() > 0) {
 295                 final Wrap<? extends Text> secondaryText = lookup.lookup(Text.class, new ByStyleClass("secondary-text")).wrap();
 296                 secondaryDate = Integer.parseInt(getText(secondaryText));
 297             } else {
 298                 secondaryDate = -1;
 299             }
 300         }
 301 
 302         @Override
 303         public String toString() {
 304             return String.format("%d", mainDate);
 305         }
 306     }
 307 
 308     public class PopupInfoDescription {
 309 
 310         public final String monthName;
 311         public final String year;
 312         public final String secondaryLabel;
 313         public final List<Integer> weekNumbers;
 314         public final List<String> dayNames;
 315         public final List<DateCellDescription> previousMonthDays;
 316         public final List<DateCellDescription> currentMonthDays;
 317         public final List<DateCellDescription> nextMonthDays;
 318         public final DateCellDescription today;
 319         public final DateCellDescription selectedDay;
 320         public final DateCellDescription focusedDay;
 321 
 322         public PopupInfoDescription(PopupSceneDescription sceneDescription) {
 323             if (sceneDescription.today != null) {
 324                 today = new DateCellDescription(sceneDescription.today);
 325             } else {
 326                 today = null;
 327             }
 328 
 329             monthName = getLabelText(sceneDescription.currentMonthWrap);
 330             year = getLabelText(sceneDescription.currentYearWrap);
 331 
 332             if (sceneDescription.weeksNumbers.size() > 0) {
 333                 assertEquals(sceneDescription.weeksNumbers.size(), 6);
 334             }
 335 
 336             weekNumbers = new ArrayList();
 337             for (Wrap<? extends DateCell> dateCell : sceneDescription.weeksNumbers) {
 338                 weekNumbers.add(new DateCellDescription(dateCell).mainDate);
 339             }
 340 
 341             currentMonthDays = new ArrayList();
 342             for (Wrap<? extends DateCell> cell : sceneDescription.currentMonthDays) {
 343                 currentMonthDays.add(new DateCellDescription(cell));
 344             }
 345 
 346             previousMonthDays = new ArrayList();
 347             for (Wrap<? extends DateCell> cell : sceneDescription.previousMonthDays) {
 348                 previousMonthDays.add(new DateCellDescription(cell));
 349             }
 350 
 351             nextMonthDays = new ArrayList();
 352             for (Wrap<? extends DateCell> cell : sceneDescription.nextMonthDays) {
 353                 nextMonthDays.add(new DateCellDescription(cell));
 354             }
 355 
 356             if (sceneDescription.secondaryLabel != null) {
 357                 secondaryLabel = getLabelText(sceneDescription.secondaryLabel);
 358             } else {
 359                 secondaryLabel = null;
 360             }
 361 
 362             dayNames = new ArrayList();
 363             assertEquals(sceneDescription.daysNames.size(), 7);
 364             for (Wrap<? extends DateCell> cell : sceneDescription.daysNames) {
 365                 dayNames.add(getText(cell.as(Parent.class, Node.class).lookup(Text.class).wrap()));
 366             }
 367 
 368             assertEquals(sceneDescription.nextMonthDays.size()
 369                     + sceneDescription.previousMonthDays.size()
 370                     + sceneDescription.currentMonthDays.size(),
 371                     6 * 7);
 372 
 373             selectedDay = new GetAction<DateCellDescription>() {
 374                 @Override
 375                 public void run(Object... parameters) throws Exception {
 376                     for (Wrap<? extends DateCell> cell : ((PopupSceneDescription) parameters[0]).currentMonthDays) {
 377                         if (cell.getControl().getStyleClass().contains("selected")) {
 378                             setResult(new DateCellDescription(cell));
 379                         }
 380                     }
 381                 }
 382             }.dispatch(testedControl.getEnvironment(), sceneDescription);
 383 
 384             focusedDay = new GetAction<DateCellDescription>() {
 385                 @Override
 386                 public void run(Object... parameters) throws Exception {
 387                     for (Wrap<? extends DateCell> cell : ((PopupSceneDescription) parameters[0]).previousMonthDays) {
 388                         if (cell.getControl().isFocused()) {
 389                             setResult(new DateCellDescription(cell));
 390                         }
 391                     }
 392                     for (Wrap<? extends DateCell> cell : ((PopupSceneDescription) parameters[0]).currentMonthDays) {
 393                         if (cell.getControl().isFocused()) {
 394                             setResult(new DateCellDescription(cell));
 395                         }
 396                     }
 397                     for (Wrap<? extends DateCell> cell : ((PopupSceneDescription) parameters[0]).nextMonthDays) {
 398                         if (cell.getControl().isFocused()) {
 399                             setResult(new DateCellDescription(cell));
 400                         }
 401                     }
 402                 }
 403             }.dispatch(testedControl.getEnvironment(), sceneDescription);
 404         }
 405     }
 406 
 407     public class PopupSceneDescription {
 408 
 409         public List<Wrap<? extends DateCell>> previousMonthDays;
 410         public List<Wrap<? extends DateCell>> currentMonthDays;
 411         public List<Wrap<? extends DateCell>> nextMonthDays;
 412         public List<Wrap<? extends DateCell>> weeksNumbers;
 413         public List<Wrap<? extends DateCell>> daysNames;
 414         public Wrap<? extends Button> nextMonthWrap;
 415         public Wrap<? extends Label> currentMonthWrap;
 416         public Wrap<? extends Button> previousMonthWrap;
 417         public Wrap<? extends Button> nextYearWrap;
 418         public Wrap<? extends Label> currentYearWrap;
 419         public Wrap<? extends Button> previousYearWrap;
 420         public Wrap<? extends DateCell> today;
 421         public Wrap<? extends Label> secondaryLabel;
 422 
 423         /**
 424          * Performs the exploration of the popup and wraps it graphical
 425          * components
 426          */
 427         public void extractData() throws InterruptedException {
 428             checkPopupVisibility(true);
 429             popupScene = getPopupWrap();
 430             Parent<Node> sceneParent = popupScene.as(Parent.class, Node.class);
 431 
 432             previousMonthWrap = sceneParent.lookup(Button.class, new ByStyleClass("left-button")).wrap(0);
 433             previousYearWrap = sceneParent.lookup(Button.class, new ByStyleClass("left-button")).wrap(1);
 434 
 435             nextMonthWrap = sceneParent.lookup(Button.class, new ByStyleClass("right-button")).wrap(0);
 436             nextYearWrap = sceneParent.lookup(Button.class, new ByStyleClass("right-button")).wrap(1);
 437 
 438             currentMonthWrap = sceneParent.lookup(Label.class, new ByStyleClass("spinner-label")).wrap(0);
 439             currentYearWrap = sceneParent.lookup(Label.class, new ByStyleClass("spinner-label")).wrap(1);
 440 
 441             weeksNumbers = new ArrayList<Wrap<? extends DateCell>>();
 442             Lookup weekNumbersLookup = sceneParent.lookup(DateCell.class, new ByStyleClass("week-number-cell"));
 443             for (int i = 0; i < weekNumbersLookup.size(); i++) {
 444                 weeksNumbers.add(weekNumbersLookup.wrap(i));
 445             }
 446 
 447             previousMonthDays = new ArrayList<Wrap<? extends DateCell>>();
 448             currentMonthDays = new ArrayList<Wrap<? extends DateCell>>();
 449             nextMonthDays = new ArrayList<Wrap<? extends DateCell>>();
 450             Lookup monthDaysLookup = sceneParent.lookup(DateCell.class, new ByStyleClass("day-cell"));
 451             for (int i = 0; i < monthDaysLookup.size(); i++) {
 452                 if (((Wrap<? extends DateCell>) monthDaysLookup.wrap(i)).getControl().getStyleClass().contains("previous-month")) {
 453                     previousMonthDays.add(monthDaysLookup.wrap(i));
 454                 } else if (((Wrap<? extends DateCell>) monthDaysLookup.wrap(i)).getControl().getStyleClass().contains("next-month")) {
 455                     nextMonthDays.add(monthDaysLookup.wrap(i));
 456                 } else {
 457                     currentMonthDays.add(monthDaysLookup.wrap(i));
 458                 }
 459             }
 460 
 461             daysNames = new ArrayList<Wrap<? extends DateCell>>();
 462             Lookup daysNamesLookup = sceneParent.lookup(DateCell.class, new ByStyleClass("day-name-cell"));
 463             for (int i = 0; i < daysNamesLookup.size(); i++) {
 464                 daysNames.add(daysNamesLookup.wrap(i));
 465             }
 466 
 467             final Lookup todayLookup = sceneParent.lookup(DateCell.class, new ByStyleClass("today"));
 468             if (todayLookup.size() > 0) {
 469                 today = todayLookup.wrap();
 470             } else {
 471                 today = null;
 472             }
 473 
 474             final Lookup secondaryLabelLookup = sceneParent.lookup(Label.class, new ByStyleClass("secondary-text"));
 475             if (secondaryLabelLookup.size() > 0) {
 476                 secondaryLabel = todayLookup.wrap();
 477             } else {
 478                 secondaryLabel = null;
 479             }
 480         }
 481 
 482         public PopupInfoDescription getInfoDescription() throws InterruptedException {
 483             extractData();
 484             return new PopupInfoDescription(this);
 485         }
 486     }
 487 
 488     protected String getControlText() {
 489         final Wrap<? extends TextField> tf = testedControl.as(Parent.class, Node.class).lookup(TextField.class).wrap();
 490         return new GetAction<String>() {
 491             @Override
 492             public void run(Object... os) throws Exception {
 493                 setResult(tf.getControl().getText());
 494             }
 495         }.dispatch(testedControl.getEnvironment());
 496     }
 497 
 498     protected void setDefaultDate() {
 499         setDate(DEFAULT_DATE);
 500     }
 501 
 502     protected LocalDate getDate() {
 503         return new GetAction<LocalDate>() {
 504             @Override
 505             public void run(Object... os) throws Exception {
 506                 setResult(testedControl.getControl().getValue());
 507             }
 508         }.dispatch(testedControl.getEnvironment());
 509     }
 510 
 511     protected void setDate(final LocalDate date) {
 512         new GetAction() {
 513             @Override
 514             public void run(Object... os) throws Exception {
 515                 testedControl.getControl().setValue(date);
 516             }
 517         }.dispatch(testedControl.getEnvironment());
 518     }
 519 
 520     protected void setChronology(final Chronology ch) {
 521         new GetAction() {
 522             @Override
 523             public void run(Object... os) throws Exception {
 524                 testedControl.getControl().setChronology(ch);
 525             }
 526         }.dispatch(testedControl.getEnvironment());
 527     }
 528 
 529     protected void waitShownText(final String expectedText) {
 530         testedControl.waitState(new State<String>() {
 531             public String reached() {
 532                 return getControlText();
 533             }
 534 
 535             @Override
 536             public String toString() {
 537                 return String.format("Waiting [%s] to be shown in the editor."
 538                         + "Got [%s]", expectedText, getControlText());
 539             }
 540         }, expectedText);
 541     }
 542 
 543     void checkExpectedDate(LocalDate expected) {
 544         LocalDate actual = new GetAction<LocalDate>() {
 545             @Override public void run(Object... parameters) throws Exception {
 546                 setResult(testedControl.getControl().getValue());
 547             }
 548         }.dispatch(testedControl.getEnvironment());
 549         assertEquals(expected, actual);
 550     }
 551 
 552     private int determineDaysInMonth(YearMonth month) {
 553         return month.atDay(1).plusMonths(1).minusDays(1).getDayOfMonth();
 554     }
 555 
 556     protected void getDays() {
 557     }
 558 
 559     private String getText(final Wrap<? extends Text> textNodeWrap) {
 560         return new GetAction<String>() {
 561             @Override
 562             public void run(Object... os) throws Exception {
 563                 setResult(textNodeWrap.getControl().getText());
 564             }
 565         }.dispatch(textNodeWrap.getEnvironment());
 566     }
 567 
 568     private String getLabelText(final Wrap<? extends Label> labelNodeWrap) {
 569         return new GetAction<String>() {
 570             @Override
 571             public void run(Object... os) throws Exception {
 572                 setResult(labelNodeWrap.getControl().getText());
 573             }
 574         }.dispatch(labelNodeWrap.getEnvironment());
 575     }
 576 
 577     protected enum Properties {
 578 
 579         prefWidth, prefHeight, chronology, dayCellFactory,
 580         converter, showWeekNumbers, hover, showing, editable,
 581         prompttext,   focused, pressed, armed, width}
 582 
 583     /**
 584      * By providing correct map of checked fields it is possible to examine
 585      * PopupInfoDescription instance.
 586      */
 587     public static class DateState implements State<Boolean> {
 588 
 589         Map<String, String> expectedState;
 590         Map<String, String> actualState;
 591         PopupSceneDescription description;
 592         PopupInfoDescription info;
 593 
 594         public DateState(Map<String, String> expectedState, PopupSceneDescription description) {
 595             this.expectedState = expectedState;
 596             this.description = description;
 597             actualState = new HashMap<String, String>(expectedState.size());
 598         }
 599 
 600         public Boolean reached() {
 601             boolean isReached = true;
 602             try {
 603                 info = description.getInfoDescription();
 604                 for (String checkedField : expectedState.keySet()) {
 605                     actualState.put(checkedField, String.valueOf(info.getClass().getField(checkedField).get(info)).toLowerCase());
 606                     isReached &= expectedState.get(checkedField).toLowerCase().equals(actualState.get(checkedField));
 607                 }
 608             } catch(InterruptedException iex) {
 609                 Logger.getLogger(TestBase.class.getName()).log(Level.SEVERE, null, iex);
 610             } finally {
 611                return isReached ? Boolean.TRUE : null;
 612             }
 613         }
 614 
 615         @Override
 616         public String toString() {
 617             StringBuilder sb = new StringBuilder("[");
 618             for (String key : expectedState.keySet()) {
 619                 sb.append(String.format("Expected: [%s], Actual: [%s]", expectedState.get(key), actualState.get(key)));
 620                 sb.append("\n");
 621             }
 622 
 623             sb.append("]");
 624             return sb.toString();
 625         }
 626     }
 627 
 628     /*
 629      * Context menu methods.
 630      */
 631 
 632     final Lookup<Scene> contextMenuLookup = Root.ROOT.lookup(
 633             new LookupCriteria<Scene>() {
 634                 public boolean check(Scene cntrl) {
 635                     return cntrl.getWidth() <= 175.0 + (Utils.isLinux() ? 7 : 0)
 636                             && cntrl.getHeight() <= 82.0 + (Utils.isLinux() ? 5 : 0);
 637                 }
 638     });
 639 
 640     /*
 641      * Makes right mouse click on the popup.
 642      */
 643     void openContextMenu() throws InterruptedException {
 644         waitPopupShowingState(true);
 645         final Wrap<? extends Scene> popupWrap = getPopupWrap();
 646         popupWrap.mouse().click(1, popupWrap.getClickPoint(), MouseButtons.BUTTON3);
 647         waitPopupShowingState(true);
 648     }
 649 
 650     void waitContextMenuShowing(final boolean showing) {
 651         testedControl.waitState(new State<Boolean>() {
 652             public Boolean reached() {
 653                 int expected = showing ? 1 : 0;
 654                 return (contextMenuLookup.size() == expected) ? Boolean.TRUE : null;
 655             }
 656 
 657             @Override
 658             public String toString() { return String.format("[Context menu is showing = %b]", showing); }
 659         });
 660     }
 661 
 662     Wrap getMenuItemLabelWrap(final String itemText) {
 663         if (null == itemText) {
 664             throw new IllegalArgumentException("[Cannot lookup 'null' menu item]");
 665         }
 666 
 667         Wrap<? extends Scene> wrap = contextMenuLookup.wrap();
 668         Lookup lookup = wrap.as(Parent.class, Label.class).lookup(Label.class, new LookupCriteria<Label>() {
 669             public boolean check(Label control) {
 670                 return control.getParent().getStyleClass().contains("menu-item")
 671                        && itemText.equals(control.getText());
 672             }
 673         });
 674 
 675         assertEquals("[Context menu item 'Show Today' not fonud]", 1, lookup.size());
 676 
 677         return lookup.wrap();
 678     }
 679 
 680     Wrap getMenuItemWrap(final String itemText) {
 681         Wrap<? extends Scene> wrap = contextMenuLookup.wrap();
 682         Lookup lookup = wrap.as(Parent.class, Region.class).lookup(Region.class, new LookupCriteria<Region>() {
 683             public boolean check(Region control) {
 684 
 685                 if (!control.getStyleClass().contains("menu-item")) return false;
 686 
 687                 for (Object object : control.getChildrenUnmodifiable()) {
 688                     if (Label.class.isAssignableFrom(object.getClass())
 689                         && itemText.equals(((Label) object).getText())) {
 690 
 691                             return true;
 692                     }
 693                 }
 694 
 695                 return false;
 696             }
 697         });
 698 
 699         assertEquals(String.format("[Context menu item '%s' not fonud]", itemText), 1, lookup.size());
 700 
 701         return lookup.wrap();
 702     }
 703 
 704     void contextMenuShowToday() throws InterruptedException {
 705         openContextMenu();
 706         getMenuItemWrap("Show Today").mouse().click();
 707     }
 708 
 709     void contextMenuShowWeekNumbers() throws InterruptedException {
 710         openContextMenu();
 711         getMenuItemWrap("Show Week Numbers").mouse().click();
 712     }
 713 
 714     Wrap<? extends Scene> getContextMenuWrap() {
 715         return contextMenuLookup.wrap();
 716     }
 717 
 718     Wrap getContextMenuContentWrap() {
 719         return getContextMenuWrap().as(Parent.class, Region.class)
 720                .lookup(Region.class, new ByStyleClass("context-menu")).wrap();
 721     }
 722 
 723     /*
 724      * Getter and setter wrappers
 725      */
 726     void setDayCellFactory(Callback<DatePicker, DateCell> dayCellFactory) {
 727         new GetAction<Void>() {
 728             @Override public void run(Object... parameters) throws Exception {
 729                 testedControl.getControl().setDayCellFactory((Callback<DatePicker, DateCell>) parameters[0]);
 730             }
 731         }.dispatch(testedControl.getEnvironment(), dayCellFactory);
 732     }
 733 
 734     Callback<DatePicker, DateCell> getDayCellFactory() {
 735         return new GetAction<Callback<DatePicker, DateCell>>() {
 736             @Override public void run(Object... parameters) throws Exception {
 737                 setResult(testedControl.getControl().getDayCellFactory());
 738             }
 739         }.dispatch(testedControl.getEnvironment());
 740     }
 741 
 742     void setConverter(StringConverter<LocalDate> converter) {
 743         new GetAction<Void>() {
 744             @Override public void run(Object... parameters) throws Exception {
 745                 testedControl.getControl().setConverter((StringConverter<LocalDate>) parameters[0]);
 746             }
 747         }.dispatch(testedControl.getEnvironment(), converter);
 748     }
 749 
 750     StringConverter<LocalDate> getConverter() {
 751         return new GetAction<StringConverter>() {
 752             @Override public void run(Object... parameters) throws Exception {
 753                 setResult(testedControl.getControl().getConverter());
 754             }
 755         }.dispatch(testedControl.getEnvironment());
 756     }
 757 }