Commit bb048d92 authored by Swergas's avatar Swergas

refactoring: replace some implicit waits by explicit waits

parent 68cd6f41
...@@ -10,12 +10,9 @@ import subprocess ...@@ -10,12 +10,9 @@ import subprocess
import re import re
from selenium import webdriver from selenium import webdriver
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.alert import Alert from selenium.webdriver.common.alert import Alert
from util.fake_sent_emails_manager import FakeSentEmailsManager from util.fake_sent_emails_manager import FakeSentEmailsManager
from util.selenium_tools import element_has_non_empty_content, wait_for_element_exists_and_contains_expected_text from util.selenium_tools import wait_for_element_exists, wait_for_elements_exist, wait_for_element_exists_and_contains_expected_text, wait_for_element_exists_and_has_non_empty_content
SERVER_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY = "demo/run-server.sh" SERVER_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY = "demo/run-server.sh"
...@@ -24,7 +21,8 @@ DATABASE_FOLDER_PATH_RELATIVE_TO_GIT_REPOSITORY = "_run/spool" ...@@ -24,7 +21,8 @@ DATABASE_FOLDER_PATH_RELATIVE_TO_GIT_REPOSITORY = "_run/spool"
FAKE_SENDMAIL_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY = "tests/tools/sendmail_fake.sh" FAKE_SENDMAIL_EXECUTABLE_FILE_PATH_RELATIVE_TO_GIT_REPOSITORY = "tests/tools/sendmail_fake.sh"
SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH = "/tmp/sendmail_fake" SENT_EMAILS_TEXT_FILE_ABSOLUTE_PATH = "/tmp/sendmail_fake"
USE_HEADLESS_BROWSER = True # Set this to True if you run this test in Continuous Integration (it has no graphical display) USE_HEADLESS_BROWSER = True # Set this to True if you run this test in Continuous Integration (it has no graphical display)
WAIT_TIME_BETWEEN_EACH_STEP = 0.05 # In seconds (float) WAIT_TIME_BETWEEN_EACH_STEP = 0.05 # In seconds (float). Time we wait between each action that we tell Selenium driver to do in the browser. This referes to Selenium's "Implicit Wait" concept
EXPLICIT_WAIT_TIMEOUT = 10 # In seconds. Maximum duration Selenium driver will wait for appearance of a specific DOM element expected in the page (for example when transitioning from a page to another). This referes to Selenium's "Explicit Wait" concept
NUMBER_OF_INVITED_VOTERS = 20 # This is N in description of Scenario 1. N is between 6 (quick test) and 1000 (load testing) NUMBER_OF_INVITED_VOTERS = 20 # This is N in description of Scenario 1. N is between 6 (quick test) and 1000 (load testing)
NUMBER_OF_VOTING_VOTERS = 10 # This is K in description of Scenario 1. K is between 6 (quick test) and 1000 (load testing). K <= N. (Some invited voters don't vote, this is abstention, and its value is N - K) NUMBER_OF_VOTING_VOTERS = 10 # This is K in description of Scenario 1. K is between 6 (quick test) and 1000 (load testing). K <= N. (Some invited voters don't vote, this is abstention, and its value is N - K)
...@@ -418,22 +416,19 @@ class BeleniosTestElectionScenario1(unittest.TestCase): ...@@ -418,22 +416,19 @@ class BeleniosTestElectionScenario1(unittest.TestCase):
page_title_css_selector = "#header h1" page_title_css_selector = "#header h1"
page_title_expected_content = "Administration" page_title_expected_content = "Administration"
page_title_element = wait_for_element_exists_and_contains_expected_text(browser, page_title_css_selector, page_title_expected_content) wait_for_element_exists_and_contains_expected_text(browser, page_title_css_selector, page_title_expected_content)
assert page_title_element
def log_out(self): def log_out(self):
browser = self.browser browser = self.browser
# In the header of the page, she clicks on the "Log out" link # In the header of the page, she clicks on the "Log out" link
logout_link_css_id = "logout" logout_link_css_selector = "#logout"
logout_element = browser.find_element_by_id(logout_link_css_id) logout_element = wait_for_element_exists(browser, logout_link_css_selector, EXPLICIT_WAIT_TIMEOUT)
logout_element.click() logout_element.click()
wait_a_bit()
# She arrives back on the logged out home page. She checks that a login link is present # She arrives back on the logged out home page. She checks that a login link is present
login_link_id = "login_local" login_link_css_selector = "#login_local"
browser.find_element_by_id(login_link_id) wait_for_element_exists(browser, login_link_css_selector, EXPLICIT_WAIT_TIMEOUT)
def administrator_creates_election(self): def administrator_creates_election(self):
...@@ -783,38 +778,23 @@ pris en compte. ...@@ -783,38 +778,23 @@ pris en compte.
# Bob has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL. # Bob has received 2 emails containing an invitation to vote and all necessary credentials (election page URL, username, password). He goes to the election page URL.
browser.get(voter["election_page_url"]) browser.get(voter["election_page_url"])
wait_a_bit()
# He clicks on the "Start" button # He clicks on the "Start" button
start_button_expected_label = "Start" start_button_expected_label = "Start"
start_button_css_selector = "#main button" start_button_css_selector = "#main button"
start_button_element = browser.find_element_by_css_selector(start_button_css_selector) start_button_element = wait_for_element_exists_and_contains_expected_text(browser, start_button_css_selector, start_button_expected_label)
verify_element_label(start_button_element, start_button_expected_label)
start_button_element.click() start_button_element.click()
wait_a_bit()
# A loading screen appears, then another screen appears. He clicks on the "Here" button # A loading screen appears, then another screen appears. He clicks on the "Here" button
try: here_button_expected_label = "here"
here_button_expected_label = "here" here_button_css_selector = "#input_code button"
here_button_css_selector = "#input_code button" here_button_element = wait_for_element_exists_and_contains_expected_text(browser, here_button_css_selector, here_button_expected_label)
here_button_element = WebDriverWait(browser, 10).until( here_button_element.click()
EC.presence_of_element_located((By.CSS_SELECTOR, here_button_css_selector))
)
verify_element_label(here_button_element, here_button_expected_label)
here_button_element.click()
except Exception:
raise Exception("Could not find expected DOM element until timeout")
wait_a_bit()
# A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential. # A modal opens (it is an HTML modal created using Window.prompt()), with an input field. He types his credential.
credential_prompt = Alert(browser) credential_prompt = Alert(browser)
credential_prompt.send_keys(voter["credential"]) credential_prompt.send_keys(voter["credential"])
credential_prompt.accept() credential_prompt.accept()
wait_a_bit()
# A new screen appears, which has a title "Step 2/6: Answer to questions", and a content: # A new screen appears, which has a title "Step 2/6: Answer to questions", and a content:
# "Question 1?" # "Question 1?"
# "Question #1 of 1 — select between 1 and 2 answer(s)" # "Question #1 of 1 — select between 1 and 2 answer(s)"
...@@ -825,7 +805,8 @@ pris en compte. ...@@ -825,7 +805,8 @@ pris en compte.
# He fills his votes to each answer of the question # He fills his votes to each answer of the question
answers_css_selector = "#question_div input[type=checkbox]" answers_css_selector = "#question_div input[type=checkbox]"
answers_elements = browser.find_elements_by_css_selector(answers_css_selector) # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]") answers_elements = wait_for_elements_exist(browser, answers_css_selector) # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]")
assert len(answers_elements) is 2 assert len(answers_elements) is 2
question1_answer1_element = answers_elements[0] question1_answer1_element = answers_elements[0]
question1_answer2_element = answers_elements[1] question1_answer2_element = answers_elements[1]
...@@ -840,17 +821,12 @@ pris en compte. ...@@ -840,17 +821,12 @@ pris en compte.
if voter_vote_to_question_1_answer_2: if voter_vote_to_question_1_answer_2:
question1_answer2_element.click() question1_answer2_element.click()
wait_a_bit()
# He clicks on the "Next" button # He clicks on the "Next" button
next_button_expected_label = "Next" next_button_expected_label = "Next"
next_button_css_selector = "#question_div button" next_button_css_selector = "#question_div button"
next_button_element = browser.find_element_by_css_selector(next_button_css_selector) next_button_element = wait_for_element_exists_and_contains_expected_text(browser, next_button_css_selector, next_button_expected_label)
verify_element_label(next_button_element, next_button_expected_label)
next_button_element.click() next_button_element.click()
wait_a_bit()
""" """
A new screen appears, showing: A new screen appears, showing:
...@@ -869,9 +845,8 @@ pris en compte. ...@@ -869,9 +845,8 @@ pris en compte.
""" """
# He remembers the smart ballot tracker that is displayed. # He remembers the smart ballot tracker that is displayed.
smart_ballot_tracker_css_id = "ballot_tracker" smart_ballot_tracker_css_selector = "#ballot_tracker"
custom_wait = WebDriverWait(browser, 10) smart_ballot_tracker_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_css_selector)
smart_ballot_tracker_element = custom_wait.until(element_has_non_empty_content((By.ID, smart_ballot_tracker_css_id)))
smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute('innerText') smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute('innerText')
assert len(smart_ballot_tracker_value) > 5 assert len(smart_ballot_tracker_value) > 5
...@@ -883,11 +858,9 @@ pris en compte. ...@@ -883,11 +858,9 @@ pris en compte.
next_button_element = browser.find_element_by_css_selector(next_button_css_selector) next_button_element = browser.find_element_by_css_selector(next_button_css_selector)
next_button_element.click() next_button_element.click()
wait_a_bit()
# He types his voter username and password, and submits the form # He types his voter username and password, and submits the form
username_field_css_selector = "#main input[name=username]" username_field_css_selector = "#main input[name=username]"
username_field_element = browser.find_element_by_css_selector(username_field_css_selector) username_field_element = wait_for_element_exists(browser, username_field_css_selector)
username_field_element.send_keys(voter["username"]) username_field_element.send_keys(voter["username"])
password_field_css_selector = "#main input[name=password]" password_field_css_selector = "#main input[name=password]"
...@@ -897,9 +870,8 @@ pris en compte. ...@@ -897,9 +870,8 @@ pris en compte.
password_field_element.submit() password_field_element.submit()
# He checks that the smart ballot tracker value that appears on screen is the same as the one he noted # He checks that the smart ballot tracker value that appears on screen is the same as the one he noted
smart_ballot_tracker_verification_css_id = "ballot_tracker" smart_ballot_tracker_verification_css_selector = "#ballot_tracker"
smart_ballot_tracker_verification_custom_wait = WebDriverWait(browser, 20) smart_ballot_tracker_verification_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_verification_css_selector, 20)
smart_ballot_tracker_verification_element = smart_ballot_tracker_verification_custom_wait.until(element_has_non_empty_content((By.ID, smart_ballot_tracker_verification_css_id)))
smart_ballot_tracker_verification_value = smart_ballot_tracker_verification_element.get_attribute('innerText') smart_ballot_tracker_verification_value = smart_ballot_tracker_verification_element.get_attribute('innerText')
assert len(smart_ballot_tracker_verification_value) > 5 assert len(smart_ballot_tracker_verification_value) > 5
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# -*- coding: utf-8 -* # -*- coding: utf-8 -*
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import StaleElementReferenceException from selenium.common.exceptions import StaleElementReferenceException
...@@ -72,7 +73,35 @@ def wait_for_element_exists_and_contains_expected_text(browser, css_selector, ex ...@@ -72,7 +73,35 @@ def wait_for_element_exists_and_contains_expected_text(browser, css_selector, ex
try: try:
ignored_exceptions = (NoSuchElementException, StaleElementReferenceException,) ignored_exceptions = (NoSuchElementException, StaleElementReferenceException,)
custom_wait = WebDriverWait(browser, wait_duration, ignored_exceptions=ignored_exceptions) custom_wait = WebDriverWait(browser, wait_duration, ignored_exceptions=ignored_exceptions)
page_title_element = custom_wait.until(element_exists_and_contains_expected_text((By.CSS_SELECTOR, css_selector), expected_text)) element = custom_wait.until(element_exists_and_contains_expected_text((By.CSS_SELECTOR, css_selector), expected_text))
return page_title_element return element
except Exception: except Exception:
raise Exception("Could not find expected DOM element '" + css_selector + "' with text content '" + expected_text + "' until timeout of " + str(wait_duration) + " seconds") raise Exception("Could not find expected DOM element '" + css_selector + "' with text content '" + expected_text + "' until timeout of " + str(wait_duration) + " seconds")
def wait_for_element_exists_and_has_non_empty_content(browser, css_selector, wait_duration=10):
try:
ignored_exceptions = (NoSuchElementException, StaleElementReferenceException,)
custom_wait = WebDriverWait(browser, wait_duration, ignored_exceptions=ignored_exceptions)
element = custom_wait.until(element_has_non_empty_content((By.CSS_SELECTOR, css_selector)))
return element
except Exception:
raise Exception("Could not find expected DOM element '" + css_selector + "' with non-empty content until timeout of " + str(wait_duration) + " seconds")
def wait_for_element_exists(browser, css_selector, wait_duration=10):
try:
return WebDriverWait(browser, wait_duration).until(
EC.presence_of_element_located((By.CSS_SELECTOR, css_selector))
)
except Exception:
raise Exception("Could not find expected DOM element '" + css_selector + "' until timeout of " + str(wait_duration) + " seconds")
def wait_for_elements_exist(browser, css_selector, wait_duration=10):
try:
return WebDriverWait(browser, wait_duration).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, css_selector))
)
except Exception:
raise Exception("Could not find expected DOM elements '" + css_selector + "' until timeout of " + str(wait_duration) + " seconds")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment