1

My HTML looks like below

<div class="container">
    <div class="jumbotron">
        <h1 id="survey-title" class="display-5 text-capitalize">Your favourite candidate</h1>
        <hr class="my-4">

        <div class="card-body">
            <h5 id="question-title" class="card-title">What is your name?</h5>

            <form id="question_form" action="/survey/takesurvey/1/2" method="post" class="form">
                <input type="hidden" name="csrfmiddlewaretoken" value="ELBWYGZRxRuI7CoZ2xkmgCJ3f9JweFMM4Ew1pQbgSE3BLb38VStPJEbHvsyiBEFg">
                <div class="form-group"><input type="text" name="response" value="asdfasdfas" maxlength="400" class="form-control" title="" id="id_response"></div>
            </form>
        </div>

        <a id="btn-previous" href="javascript:history.back()" class="btn btn-primary ">Previous</a>
        <a id="btn-next" href="/survey/takesurvey/finish/1" class="btn btn-primary">Next</a>
    </div>
</div>

Notice the href on previous button

<a id="btn-previous" href="javascript:history.back()" class="btn btn-primary ">Previous</a>

Clicking next will submit form and go to "next" page, again clicking "previous" should come back and preserve the input value in previous page. I am using following code to test this behavior, The following code is working only when I put sleep(1)line in between finding the element and clicking. Else the page is not going back (url not changing)

  • Why just waiting for url change before clicking the button not working
  • I suspect it has to do with javascript event handler (which is inline in html) not being loaded.
  • I is there any way to check if page is ready for the click event
cur_url = self.browser.current_url
self.browser.find_element_by_id('btn-next').click()

wait(lambda: self.assertNotEqual(self.browser.current_url, cur_url))
btn_previous = self.browser.find_element_by_id('btn-previous')

## This code is not working without the sleep below. Which I suspect 
## has to do with javascript event handler (which is inline in html) not being loaded.
## Is there better way to check that the button is ready to be clicked
sleep(1)

btn_previous.click()
2
  • If you're doing testing that should go in a onclick rather than href. If you're not there's an easier way to go back. Commented Jan 12, 2020 at 5:44
  • It is same when I put that in onclick, and as you may noticed its working when I put sleep(1) Commented Jan 12, 2020 at 6:32

3 Answers 3

1

You can use WebDriverWait with element_to_be_clickable condition. Check here also.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait(self.browser, 5).until(EC.element_to_be_clickable((By.ID, "btn-previous"))).click()

You can try wait for JavaScript:

WebDriverWait(self.browser, 20).until(lambda d: d.execute_script(
        'return (document.readyState == "complete" || document.readyState == "interactive")'))

# or without interactive 
WebDriverWait(self.browser, 20).until(lambda d: d.execute_script(
        'return document.readyState == "complete"'))
Sign up to request clarification or add additional context in comments.

3 Comments

thank you for your solution but it has same result as my previous code. The code goes past the line for browser url doesn't change. It works only when using sleep(1) before the line
Check answer update with waiting for JS to complete
thanks, yes solution of checking readyState worked when putting it after clicking button
0

This is not the desired solution but a workaround the worked for me for now.

My functions for wait

from time import sleep
from functools import wraps
from selenium.common.exceptions import NoSuchElementException

def wrap_in_wait(func, retires=5):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return wait(func, *args, retires=retires, **kwargs)
    return wrapper

def wait(func, *args, retires=10, exceptions=[AssertionError, NoSuchElementException], **kwargs):
    for i in range(retires):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            print('retry', i)
            if type(e) not in exceptions:
                raise e

            if i == retires-1:
                raise e

            sleep(0.5 + i*.2)


Test function with Work around solution

def test_question_form_is_loaded_with_existing_answer(self):
        '''Test question form pre-loads'''
        self.browser.get(self.live_server_url + self.url)
        inp = self.browser.find_element_by_css_selector("input#id_response")
        inp.send_keys("This is my answer")

        self.browser.find_element_by_id('btn-next').click()

        @wrap_in_wait
        def click_previous_button():
            btn_previous = self.browser.find_element_by_id('btn-previous')
            btn_previous.click()
            inp = self.browser.find_element_by_css_selector("input#id_response")
            self.assertEqual(inp.get_attribute('value'), "This is my answer")

        click_previous_button()

This solution retries click_previous_button for 10 times if it raises AssertionError or NoSuchElementException exception. It is working on 2nd try.

But I am still not sure why javascript event is not triggered even when the button is clickable and if there is direct way to test when javascript is ready

Comments

0

I still had this issue in Selenium 4.x

My solution was (.NET):

Actions action = new Actions(Driver);
action.MoveToElement(elementToClick).Click().Perform();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.