4

I am having an issue trying to select a return date due to the fact that it cannot find the element of current_date = calendar.find_element_by_class_name("ui-datepicker-days-cell-over"). For the depart date it works but not return date. This is because when you open up the datepicker for return date, it automatically has a date highlighted for you (this is the element), but for return date it doesn't have a date highlighted when its datepicker is opened (only after you select a date).

So my question is does anybody know how to fix the code below so that it is able to retrieve the date selected from the depart datepicker (the date selected is the next_available_date) and somehow store it in the return date picker so that it becomes a selected date in the return datepicker?

At the moment the code below is able to retrieve the next available date from the depart datepicker no problem, the issue is just the return datepicker. (Both blocks of code are pretty much mirrored as each other)

# select depart date
datepicker = driver.find_element_by_id("ctl00_centralDynamicContent_OutDateTextBox")
actions.move_to_element(datepicker).click().perform()

# find the calendar, month and year picker and the current date
calendar = driver.find_element_by_id("ui-datepicker-div")
month_picker = Select(calendar.find_element_by_class_name("ui-datepicker-month"))
year_picker = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
current_date = calendar.find_element_by_class_name("ui-datepicker-days-cell-over")

# printing out current date
month = month_picker.first_selected_option.text
year = year_picker.first_selected_option.text
print("Current departure date: {day} {month} {year}".format(day=current_date.text, month=month, year=year))

# see if we have an available date in this month
try:
    next_available_date = current_date.find_element_by_xpath("following::td[@title='Click to see flights on this date' and ancestor::div/@id='ui-datepicker-div']")
    print("Found an available departure date: {day} {month} {year}".format(day=next_available_date.text, month=month, year=year))
    next_available_date.click()
except NoSuchElementException:
# looping over until the next available date found
        while True:
# click next, if not found, select the next year
            try:
                calendar.find_element_by_class_name("ui-datepicker-next").click()
            except NoSuchElementException:
# select next year
                year = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
                year.select_by_visible_text(str(int(year.first_selected_option.text) + 1))

# reporting current processed month and year
                month = Select(calendar.find_element_by_class_name("ui-datepicker-month")).first_selected_option.text
                year = Select(calendar.find_element_by_class_name("ui-datepicker-year")).first_selected_option.text
                print("Processing {month} {year}".format(month=month, year=year))

            try:
                next_available_date = calendar.find_element_by_xpath(".//td[@title='Click to see flights on this date']")
                print("Found an available departure date: {day} {month} {year}".format(day=next_available_date.text, month=month, year=year))
                next_available_date.click()
                break
            except NoSuchElementException:
                continue



# select return date
datepicker = driver.find_element_by_id("ctl00_centralDynamicContent_InDateTextBox")
actions.move_to_element(datepicker).click().perform()

# find the calendar, month and year picker and the current date
calendar = driver.find_element_by_id("ui-datepicker-div")
month_picker = Select(calendar.find_element_by_class_name("ui-datepicker-month"))
year_picker = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
current_date = calendar.find_element_by_class_name("ui-datepicker-days-cell-over")

# printing out current date
month = month_picker.first_selected_option.text
year = year_picker.first_selected_option.text
print("Current return date: {day} {month} {year}".format(day=current_date.text, month=month, year=year))

# see if we have an available date in this month
try:
    next_available_date = current_date.find_element_by_xpath("following::td[@title='Click to see flights on this date' and ancestor::div/@id='ui-datepicker-div']")
    print("Found an available return date: {day} {month} {year}".format(day=next_available_date.text, month=month, year=year))
    next_available_date.click()
except NoSuchElementException:
# looping over until the next available date found
        while True:
# click next, if not found, select the next year
            try:
                calendar.find_element_by_class_name("ui-datepicker-next").click()
            except NoSuchElementException:
# select next year
                year = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
                year.select_by_visible_text(str(int(year.first_selected_option.text) + 1))

# reporting current processed month and year
                month = Select(calendar.find_element_by_class_name("ui-datepicker-month")).first_selected_option.text
                year = Select(calendar.find_element_by_class_name("ui-datepicker-year")).first_selected_option.text
                print("Processing {month} {year}".format(month=month, year=year))

            try:
                next_available_date = calendar.find_element_by_xpath(".//td[@title='Click to see flights on this date']")
                print("Found an available return date: {day} {month} {year}".format(day=next_available_date.text, month=month, year=year))
                next_available_date.click()
                break
            except NoSuchElementException:
                continue
BruceyBandit
  • 3,978
  • 19
  • 72
  • 144

1 Answers1

2

Since I remember the target site you are working with, here is an idea (not tested).

The datepicker sets the initial date to what is in the "readonly" date input. Let's remove the readonly attribute and set the return date input value to the previously selected depart date:

from datetime import datetime

# get depart date
depart_date = datetime.strptime(" ".join([next_available_date.text, month, year]), "%d %b %Y")
initial_return_date = depart_date.strftime("%d-%m-%Y")

return_date_input = driver.find_element_by_id("ctl00_centralDynamicContent_OutDateTextBox")
# remove readonly attribute
driver.execute_script("arguments[0].removeAttribute('readonly','readonly');", return_date_input)
return_date_input.send_keys(initial_return_date)

# open datepicker

Here is a quick example (using jet2.com) where the return date input value is set to what the depart date was selected:

from datetime import datetime

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


FROM = "Leeds Bradford"
TO = "Antalya"

driver = webdriver.Firefox()
driver.get("http://jet2.com/")
driver.maximize_window()

wait = WebDriverWait(driver, 90)
actions = ActionChains(driver)

# wait for the page to load
wait.until(EC.element_to_be_clickable((By.ID, "return-flight-selector")))

# fill out the form
return_flight = driver.find_element_by_id('return-flight-selector').click()

depart_from = driver.find_element_by_id("departure-airport-input")
depart_from.clear()
depart_from.click()
depart_from.send_keys(FROM)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#ui-id-1 .ui-menu-item"))).click()

go_to = driver.find_element_by_id("destination-airport-input")
go_to.send_keys(TO)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#ui-id-2 .ui-menu-item"))).click()

# select depart date
datepicker = driver.find_element_by_id("departure-date-selector")
actions.move_to_element(datepicker).click().perform()

# find the calendar, month and year picker and the current date
calendar = driver.find_element_by_id("departureDateContainer")
month_picker = Select(calendar.find_element_by_class_name("ui-datepicker-month"))
year_picker = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
current_date = calendar.find_element_by_class_name("ui-datepicker-days-cell-over")

# printing out current date
month = month_picker.first_selected_option.text
year = year_picker.first_selected_option.text
print("Current departure date: {day} {month} {year}".format(day=current_date.text, month=month, year=year))

# see if we have an available date in this month
try:
    next_available_date = current_date.find_element_by_xpath("following::td[@title='Click to see flights on this date' and ancestor::div/@id='ui-datepicker-div']")
except NoSuchElementException:
        while True:
            try:
                calendar.find_element_by_class_name("ui-datepicker-next").click()
            except NoSuchElementException:
                year_picker = Select(calendar.find_element_by_class_name("ui-datepicker-year"))
                year_picker.select_by_visible_text(str(int(year.first_selected_option.text) + 1))

            try:
                next_available_date = calendar.find_element_by_xpath(".//td[@title='Click to see flights on this date']")
                break
            except NoSuchElementException:
                continue

month_picker = Select(calendar.find_element_by_class_name("ui-datepicker-month"))
year_picker = Select(calendar.find_element_by_class_name("ui-datepicker-year"))

month = month_picker.first_selected_option.text
year = year_picker.first_selected_option.text

depart_date = datetime.strptime(" ".join([next_available_date.text, month, year]), "%d %b %Y")
next_available_date.click()

initial_return_date = depart_date.strftime("%d-%m-%Y")

return_date_input = driver.find_element_by_id("return-date-selector")
# remove readonly and disabled attributes
driver.execute_script("arguments[0].removeAttribute('disabled'); arguments[0].removeAttribute('readonly','readonly');", return_date_input)

# set the initial return date
return_date_input.clear()
return_date_input.send_keys(initial_return_date)

# open datepicker
return_date_input.click()
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • @alexce, It is actually for a different application (well technically same application). We have different environments we test in. The one you used is live in the last few weeks, this is now the test environment which we cannot get outside of work. The only difference between the two applications is actually the first page, every other page is exactly the same. I'll show you a screenshot of what this plug in looks like and the only this that is awkward is the calendar – BruceyBandit Feb 21 '16 at 12:34
  • I'll display the html as well for both depart and return dates – BruceyBandit Feb 21 '16 at 12:35
  • Displayed the images and html of the depart text box if needed. – BruceyBandit Feb 21 '16 at 12:46
  • @BruceyBandit thanks for the additional information. It looks like the answer might still be valid. Updated the code to locate the `input` with `id="ctl00_centralDynamicContent_OutDateTextBox"` that we would make editable and send the depart date to. Please try. – alecxe Feb 21 '16 at 12:47
  • Will do. Can't believe it's Sunday 1pm this afternoon and been doing testing test cases at work for over 6 hours and implementing these scripts you've been helping me with lol – BruceyBandit Feb 21 '16 at 12:54
  • @BruceyBandit well, it's almost 8 am, Sunday here and I've been working for 2 hours already :) – alecxe Feb 21 '16 at 12:55
  • @alexce Damn, I just wish I was good at programming like yourself (though i don't really have any dev experience). I tried implementing your code last 20 mins but still having same issue. I don't think I'm implementing it correctly (tbh i don't quite understand the code). If you don't mind, can you show me how it was suppose to be implemented? – BruceyBandit Feb 21 '16 at 13:24
  • @BruceyBandit first you need to understand what we are trying to do here. When we get the depart date, it is coming in day, month and year, right? We then take these components and form the `datetime` object. Then, we dump it back to string to produce the "day-month-year" formatted string - for this we use `strftime()`. Okay, now we need to paste this string into the depart input so that the calendar would pre-select it once opened. The problem is that the depart input is read only - okay, we remove the "readonly" attribute via `execute_script` and then send keys. Hope this is clear. – alecxe Feb 21 '16 at 13:30
  • Ah right, that makes sense. I'll give it another go. I know there's an issue on the depart_date as it states it needs to be an integer. I'll see what happens after setting this – BruceyBandit Feb 21 '16 at 13:43
  • @BruceyBandit ah, that's my problem, updated the code to explicitly convert the day, month and year to integers. – alecxe Feb 21 '16 at 13:50
  • @BruceyBandit updated the loading datestring into datetime code. – alecxe Feb 21 '16 at 15:53
  • @BruceyBandit hey, were you eventually able to solve it? – alecxe Feb 27 '16 at 14:02
  • I was able to solve the textbox issue where it was coming up with 'LEEDS BRADFORD' 50% of the time when opening browser, but still having issues with this date picker and the DOM unattached in meals page – BruceyBandit Feb 27 '16 at 14:23
  • @BruceyBandit gotcha, the textbox issue is though a separate one - not related to this thread, right? This one is about the datepickers - have you solved this one and has this answer helped? Thanks! – alecxe Feb 27 '16 at 14:25
  • No this one I could not get working, it keeps giving me errors stating 'actions.move_to_element(datepicker).click().perform()' where element cannot be found. This is where I place your answer above the open datepicker. That's why I want to ask you how and where you implement you answer in the code. – BruceyBandit Feb 27 '16 at 14:29
  • @BruceyBandit okay, let me see, is this still jet2.com or should I use a different URL? – alecxe Feb 27 '16 at 14:43
  • For the datepicker, It's for jet2.com on test d (not actual jet2.com) but problem is that you won't have access to it as you don't have access to the test environments. That's why I know it's difficult. For the unattached DOM you can actually use the live version www.jet2.com – BruceyBandit Feb 27 '16 at 14:46
  • @BruceyBandit okay, added a quick working example based on jet2.com. I want you to understand the idea - make the date calendar input editable and send the date text to it which would make the calendar use it as an initial value when the picker is opened. Hope it helps. Let me know in case of any questions. Thanks. – alecxe Feb 27 '16 at 15:20
  • it's working, had to do a little bit of debugging but it's fine now. Now the only issue is that DOM in the live site on the meals page where it goes unattached – BruceyBandit Feb 27 '16 at 16:07
  • @BruceyBandit okay, thanks for checking, the meals problem though is not related to this particular topic/question/problem, right? Could we solve that meals issue in a separate SO thread? (forgive me if it's already created - throw me a link in this case). Thanks. – alecxe Feb 28 '16 at 05:00
  • Here you go: http://stackoverflow.com/questions/35445294/element-no-longer-attached-to-the-dom-and-timeout-exceptions – BruceyBandit Feb 29 '16 at 21:26