package com.redheap.selenium.component; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; public class AdfCalendar extends AdfComponent { // see http://jdevadf.oracle.com/adf-richclient-demo/docs/js-subids.html private static final String SUBID_all_activities_in_view = "all_activities_in_view"; // <div> collection private static final String SUBID_all_date_links = "all_date_links"; // <a> collection private static final String SUBID_all_more_links_in_view = "all_more_links_in_view"; // <a> links for too many items private static final String SUBID_day_view_button = "day_view_button"; // af:button private static final String SUBID_list_view_button = "list_view_button"; // af:button private static final String SUBID_month_view_button = "month_view_button"; // af:button private static final String SUBID_next_button = "next_button"; // af:button private static final String SUBID_previous_button = "previous_button"; // af:button private static final String SUBID_today_button = "today_button"; // af:button private static final String SUBID_week_view_button = "week_view_button"; // af:button private static final String JS_GET_ACTIVE_DAY = JS_FIND_COMPONENT + "return comp.getActiveDay();"; private static final String JS_GET_HOUR_ZOOM = JS_FIND_COMPONENT + "return comp.getHourZoom();"; private static final String JS_GET_LIST_COUNT = JS_FIND_COMPONENT + "return comp.getListCount();"; private static final String JS_GET_SHOW_ACTIVITY_TYPES = JS_FIND_COMPONENT + "return comp.getShowActivityTypes();"; private static final String JS_GET_START_DAY_OF_WEEK = JS_FIND_COMPONENT + "return comp.getStartDayOfWeek();"; private static final String JS_GET_START_HOUR = JS_FIND_COMPONENT + "return comp.getStartHour();"; private static final String JS_GET_TIME_SLOTS_PER_HOUR = JS_FIND_COMPONENT + "return comp.getTimeSlotsPerHour();"; private static final String JS_GET_VIEW = JS_FIND_COMPONENT + "return comp.getView();"; private static final String JS_GET_ACTIVE_DAY_DOM = JS_FIND_PEER + "return peer._getActiveDayFromDom(comp);"; private final DateFormat dateformat = new SimpleDateFormat("MM/d/yyyy"); public static enum View { MONTH, DAY, WEEK, LIST }; /* also interesting: * peer._getActivityInfo(activity, gridInfo) (activity is dom elem) * gridinfo can come from _calculateMonthGridInfo(monthgrid,monthheader) both are subids * peer._getActivityLinkInList(listRow) * peer._getClientActivityId(providerId, activityId, suffix) * * */ public AdfCalendar(WebDriver webDriver, String clientid) { super(webDriver, clientid); } private String getActiveDay() { // do not expose this method as it sometimes does not return a date, but a function (in PhantomJS) return (String) executeScript(JS_GET_ACTIVE_DAY, getClientId()); } public Date getActiveDayFromDom() { String day = (String) executeScript(JS_GET_ACTIVE_DAY_DOM, getClientId()); try { Date date = dateformat.parse(day); return date; } catch (ParseException e) { throw new WebDriverException("invalid active day from dom:" + day, e); } } public Calendar getActiveDayFromDomCalendar() { Calendar retval = Calendar.getInstance(); retval.setTime(getActiveDayFromDom()); return retval; } public String getHourZoom() { return (String) executeScript(JS_GET_HOUR_ZOOM, getClientId()); } public int getListCount() { return ((Number) executeScript(JS_GET_LIST_COUNT, getClientId())).intValue(); } public List<String> getShowActivityTypes() { return (List<String>) executeScript(JS_GET_SHOW_ACTIVITY_TYPES, getClientId()); } public String getStartDayOfWeek() { return (String) executeScript(JS_GET_START_DAY_OF_WEEK, getClientId()); } public int getStartHour() { return ((Number) executeScript(JS_GET_START_HOUR, getClientId())).intValue(); } public int getTimeSlotsPerHour() { return ((Number) executeScript(JS_GET_TIME_SLOTS_PER_HOUR, getClientId())).intValue(); } public View getView() { String view = (String) executeScript(JS_GET_VIEW, getClientId()); return View.valueOf(view.toUpperCase()); } public AdfButton findDayViewButton() { return findSubIdComponent(SUBID_day_view_button); } public AdfButton findWeekViewButton() { return findSubIdComponent(SUBID_week_view_button); } public AdfButton findMonthViewButton() { return findSubIdComponent(SUBID_month_view_button); } public AdfButton findListViewButton() { return findSubIdComponent(SUBID_list_view_button); } public AdfButton findTodayButton() { return findSubIdComponent(SUBID_today_button); } public AdfButton findPreviousButton() { return findSubIdComponent(SUBID_previous_button); } public AdfButton findNextButton() { return findSubIdComponent(SUBID_next_button); } public int getActivitiesInViewCount() { List<WebElement> activitiesInView = findAllActivitiesInView(); return activitiesInView == null ? 0 : activitiesInView.size(); } public void clickActivityInView(int index) { WebElement element = findAllActivitiesInView().get(index); element.click(); waitForPpr(); } public void hoverActivityInView(int index) { WebElement element = findAllActivitiesInView().get(index); // we use pause to give javascript timer to detect hover and show popup new Actions(getDriver()).moveToElement(element).pause(1000).perform(); waitForPpr(); } public String getActivityInViewLabel(int index) { WebElement div = findAllActivitiesInView().get(index); //WebElement span = div.findElement(By.tagName("span")); return div.getText(); } public int getDateLinkCount() { List<WebElement> links = findAllDateLinks(); return links == null ? 0 : links.size(); } public void clickDateLink(int index) { WebElement element = findAllDateLinks().get(index); element.click(); waitForPpr(); } public int getMoreLinksInViewCount() { List<WebElement> moreLinks = findAllMoreLinksInView(); return moreLinks == null ? 0 : moreLinks.size(); } public void clickMoreLinkInView(int index) { WebElement element = findAllMoreLinksInView().get(index); element.click(); waitForPpr(); } public void createDayActivity(int dayOfMonth) { View view = getView(); WebElement target; switch (view) { case MONTH: final Calendar targetDay = Calendar.getInstance(); targetDay.setTime(getActiveDayFromDom()); targetDay.set(Calendar.DAY_OF_MONTH, dayOfMonth); final String xpath = "//div[@_adfdycl='" + dateformat.format(targetDay.getTime()) + "']"; target = getElement().findElement(By.xpath(xpath)).findElement(By.cssSelector(".af_calendar_month-grid-cell-header")); break; default: throw new IllegalStateException("unsupported view for creating day activity: " + view); } target.click(); waitForPpr(); } /** * Browse the calendar with previous/next buttons until the day, week or month is shown that contains the given * date. * @param date date which should be included in the view after this method completes * @throws IllegalStateException when calendar is currently in list view */ public void goToDate(final Date date) { if (getView() == View.LIST) { throw new IllegalStateException("goToDate cannot be used with list view"); } final Date targetViewStart = startOfViewPeriod(date); int direction; do { direction = targetViewStart.compareTo(startOfViewPeriod(this.getActiveDayFromDom())); if (direction < 0) { // direction < 0 = target date is still in the past. this.findPreviousButton().click(); } else if (direction > 0) { // direction > 0 = target date is still in the future. this.findNextButton().click(); } } while (direction != 0); } /** * Convert a given date to a date that would be at the beginning of the period showing in the browser with the * current view type. * For example, for day view this would only strip the time component from the given date, for week view this * would return midnight at Monday at the beginning of the week that contains the given date, for month view this * would return midnight of the first day of the month containing the given date. * @param date * @return date * @throws IllegalStateException when calendar is currently in list view */ private Date startOfViewPeriod(final Date date) { final Calendar retval = Calendar.getInstance(); retval.setTime(date); // strip time components retval.set(Calendar.HOUR, 0); retval.set(Calendar.MINUTE, 0); retval.set(Calendar.SECOND, 0); retval.set(Calendar.MILLISECOND, 0); switch (getView()) { case DAY: // nothing to do, other than time stripping break; case WEEK: retval.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); break; case MONTH: retval.set(Calendar.DAY_OF_MONTH, 1); break; case LIST: throw new IllegalStateException("startOfViewPeriod cannot be used with list view"); } return retval.getTime(); } // raw elements should not be exposed public due to ppr logic that should be part of component protected List<WebElement> findAllActivitiesInView() { return findSubIdElements(SUBID_all_activities_in_view); } // raw elements should not be exposed public due to ppr logic that should be part of component protected List<WebElement> findAllDateLinks() { return findSubIdElements(SUBID_all_date_links); } // raw elements should not be exposed public due to ppr logic that should be part of component protected List<WebElement> findAllMoreLinksInView() { return findSubIdElements(SUBID_all_more_links_in_view); } }