Commit 547f5f27 authored by Swergas's avatar Swergas

implement closing and tallying of election with threshold

parent c3297574
......@@ -117,7 +117,7 @@ Verifications all along the process is done using command line tools `belenios-t
- In the "Trustees" section, she clicks on "here". She checks that in the table on all rows, the "STATE" column is now "done"
- She clicks on the "Go back to election draft" link
- In "Validate creation" section, she clicks on the "Create election" link
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters. For example, in this test scenario, it displays 2 warnings: "Warning: No trustees were set. This means that the server will manage the election key by itself.", and "Warning: No contact was set!")
- (She arrives on the "Checklist" page, that lists all main parameters of the election for review, and that flags incoherent or misconfigured parameters.)
- She checks the presence of text "election ready"
- In the "Validate creation" section, she clicks on the "Create election" button
- (She arrives back on the "My test election for Scenario 1 — Administration" page. Its contents have changed. There is now a text saying "The election is open. Voters can vote.", and there are now buttons "Close election", "Archive election", "Delete election")
......@@ -158,6 +158,8 @@ Verifications all along the process is done using command line tools `belenios-t
- She logs in as administrator
- She clicks on the "Close election" button
- She clicks on the "Proceed to vote counting" button
- She checks the presence of text "We are now waiting for trustees... At least ${U} trustee(s) must act."
- She checks that in the table on every content row, the "DONE?" column is "No"
- She remembers the encrypted tally hash
- She remembers the link to send to each trustee, so they can tally the election
- She sends to each trustee an email containing their own link
......
......@@ -10,7 +10,7 @@ from uuid import uuid4
from distutils.util import strtobool
from selenium.common.exceptions import UnexpectedAlertPresentException
from util.fake_sent_emails_manager import FakeSentEmailsManager
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, wait_for_an_element_with_partial_link_text_exists, set_element_attribute, wait_for_element_exists_and_has_non_empty_attribute
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, wait_for_an_element_with_partial_link_text_exists, set_element_attribute, wait_for_element_exists_and_has_non_empty_attribute, verify_all_elements_have_attribute_value, verify_some_elements_have_attribute_value
from util.election_testing import console_log, random_email_addresses_generator, remove_database_folder, wait_a_bit, build_css_selector_to_find_buttons_in_page_content_by_value, initialize_server, initialize_browser, election_page_url_to_election_id, verify_election_consistency, create_election_data_snapshot, delete_election_data_snapshot, log_in_as_administrator, log_out, administrator_starts_creation_of_election, administrator_edits_election_questions, administrator_sets_election_voters, administrator_validates_creation_of_election
from util.election_test_base import BeleniosElectionTestBase
import settings
......@@ -285,7 +285,9 @@ counts."""
submit_button_element = wait_for_element_exists(browser, submit_button_css_selector)
submit_button_element.click()
trustee_link_css_selector = "#main table tr:nth-child(" + str(idx + 3) + ") td:nth-child(3) a"
wait_a_bit()
trustee_link_css_selector = "#main table tr:nth-of-type(" + str(idx + 3) + ") td:nth-of-type(3) a" # First row of table corresponds to column titles. Second row correpond to server trustee.
trustee_link_element = wait_for_element_exists_and_has_non_empty_content(browser, trustee_link_css_selector)
self.links_for_trustees.append(trustee_link_element.get_attribute('href'))
......@@ -418,7 +420,7 @@ The election administrator.\
self.browser = initialize_browser_for_scenario_2()
def administrator_starts_tallying_of_election(self):
def administrator_starts_tallying_of_election(self, with_threshold=None):
browser = self.browser
# Alice goes to the election page
......@@ -453,15 +455,30 @@ The election administrator.\
wait_a_bit()
if with_threshold is not None:
# She checks the presence of text "We are now waiting for trustees... At least ${U} trustee(s) must act."
expected_confirmation_label = "We are now waiting for trustees... At least " + str(with_threshold) + " trustee(s) must act."
expected_confirmation_css_selector = "#main"
wait_for_element_exists_and_contains_expected_text(browser, expected_confirmation_css_selector, expected_confirmation_label)
# She checks that in the table on every content row, the "DONE?" column is "No"
elements_css_selector = "#main table tr td:nth-of-type(4)"
attribute_name = "innerText"
attribute_value = "No"
verify_all_elements_have_attribute_value(browser, elements_css_selector, attribute_name, attribute_value)
# She remembers the encrypted tally hash
encrypted_tally_hash_css_selector = "#encrypted_tally_hash"
encrypted_tally_hash_element = wait_for_element_exists_and_has_non_empty_content(browser, encrypted_tally_hash_css_selector)
self.encrypted_tally_hash = encrypted_tally_hash_element.get_attribute('innerText')
# She remembers the link to send to each trustee, so they can tally the election
row_padding = 3
if with_threshold:
row_padding = 2
self.closed_election_tally_links_for_trustees = []
for idx, email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
trustee_link_css_selector = "#main table tr:nth-child(" + str(idx + 3) + ") td:nth-child(3) a"
trustee_link_css_selector = "#main table tr:nth-of-type(" + str(idx + row_padding) + ") td:nth-of-type(3) a" # First row consists in column titles. If there is no threshold for trustees, second row is for server.
trustee_link_element = wait_for_element_exists_and_has_non_empty_content(browser, trustee_link_css_selector)
self.closed_election_tally_links_for_trustees.append(trustee_link_element.get_attribute('href'))
......@@ -497,9 +514,11 @@ The election administrator.\
browser.quit()
def trustees_do_partial_decryption(self):
# Each trustee (Tom and Taylor) will do the following process:
def trustees_do_partial_decryption(self, max_trustees=None):
# Each of the `T` trustees (limited to `max_trustees`) will do the following process:
for idx, trustee_email_address in enumerate(settings.TRUSTEES_EMAIL_ADDRESSES):
if max_trustees is not None and idx >= max_trustees: # TODO: Maybe we should pick trustees randomly in the list of trustees instead of always the first few ones?
break
# He opens the link that Alice (the election administrator) has sent to him
self.browser = initialize_browser_for_scenario_2()
browser = self.browser
......@@ -566,7 +585,7 @@ The election administrator.\
browser.quit()
def administrator_finishes_tallying_of_election(self):
def administrator_finishes_tallying_of_election(self, max_trustees=None):
self.browser = initialize_browser_for_scenario_2()
browser = self.browser
......@@ -592,13 +611,14 @@ The election administrator.\
encrypted_tally_hash = encrypted_tally_hash_element.get_attribute('innerText')
assert encrypted_tally_hash == self.encrypted_tally_hash, "Error: Encrypted tally hash displayed to trustee (" + encrypted_tally_hash + ") is not the same as the one displayed to election administrator (" + self.encrypted_tally_hash + ")."
# She checks that the "DONE?" column of each trustee is to "Yes"
# She checks that the "DONE?" column of each trustee is to "Yes" (or if there is a threshold on trustees, she checks that at least `max_trustees` have a "Yes")
expected_label = "Yes"
yes_cells_selector = "#main table tr td:nth-last-child(1)"
yes_cells_elements = wait_for_elements_exist(browser, yes_cells_selector)
assert len(yes_cells_elements) > 0, "Error: could not find last cell in table in page"
for element in yes_cells_elements:
assert element.get_attribute('innerText') == expected_label, "Error: The last cell of a row in table that is displayed in page has a value that is '" + element.get_attribute('innerText') + "' instead of expected '" + expected_label + "'"
attribute_name = "innerText"
if max_trustees is None:
verify_all_elements_have_attribute_value(browser, yes_cells_selector, attribute_name, expected_label)
else:
verify_some_elements_have_attribute_value(browser, yes_cells_selector, attribute_name, expected_label, max_trustees)
# She clicks on the "Compute the result" button
compute_result_button_expected_label = "Compute the result"
......
......@@ -10,7 +10,7 @@ from uuid import uuid4
from distutils.util import strtobool
from selenium.common.exceptions import UnexpectedAlertPresentException
from util.fake_sent_emails_manager import FakeSentEmailsManager
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, wait_for_an_element_with_partial_link_text_exists, set_element_attribute, wait_for_element_exists_and_has_non_empty_attribute
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, wait_for_an_element_with_partial_link_text_exists, set_element_attribute, wait_for_element_exists_and_has_non_empty_attribute, verify_all_elements_have_attribute_value
from util.election_testing import console_log, random_email_addresses_generator, remove_database_folder, wait_a_bit, build_css_selector_to_find_buttons_in_page_content_by_value, initialize_server, initialize_browser, election_page_url_to_election_id, verify_election_consistency, create_election_data_snapshot, delete_election_data_snapshot, log_in_as_administrator, log_out, administrator_starts_creation_of_election, administrator_edits_election_questions, administrator_sets_election_voters, administrator_validates_creation_of_election
from util.election_test_base import BeleniosElectionTestBase
from test_scenario_2 import BeleniosTestElectionScenario2Base, initialize_browser_for_scenario_2
......@@ -190,7 +190,8 @@ The election administrator.\
# He clicks on the "private key" link, to download the private key (file is saved by default as `private_key.txt`)
link_css_ids = ["private_key"]
link_expected_labels = ["private key"]
self.downloaded_files_paths_per_trustee[trustee_email_address] = dict()
if trustee_email_address not in self.downloaded_files_paths_per_trustee:
self.downloaded_files_paths_per_trustee[trustee_email_address] = dict()
for idx2, link_css_id in enumerate(link_css_ids):
link_target_filename = str(uuid4())
set_element_attribute(browser, link_css_id, 'download', link_target_filename)
......@@ -356,7 +357,8 @@ The election administrator.\
# He clicks on the "public key" link and downloads the file (file is saved by default as `public_key.json`)
link_css_ids = ["public_key"]
link_expected_labels = ["public key"]
self.downloaded_files_paths_per_trustee[trustee_email_address] = dict()
if trustee_email_address not in self.downloaded_files_paths_per_trustee:
self.downloaded_files_paths_per_trustee[trustee_email_address] = dict()
for idx2, link_css_id in enumerate(link_css_ids):
link_target_filename = str(uuid4())
set_element_attribute(browser, link_css_id, 'download', link_target_filename)
......@@ -397,11 +399,9 @@ The election administrator.\
# She checks that in the table on all rows, the "STATE" column is now "done"
state_column_css_selector = "#main table tr td:last-of-type"
state_expected_label = "done"
state_cells_elements = wait_for_elements_exist(browser, state_column_css_selector)
assert len(state_cells_elements) > 0, "Error: could not find last column cells of table in page"
for element in state_cells_elements:
assert element.get_attribute('innerText') == state_expected_label, "Error: The last cell of a row in table that is displayed in page has a value that is '" + element.get_attribute('innerText') + "' instead of expected '" + state_expected_label + "'"
attribute_name = "innerText"
attribute_value = "done"
verify_all_elements_have_attribute_value(browser, state_column_css_selector, attribute_name, attribute_value)
wait_a_bit()
......@@ -495,23 +495,21 @@ The election administrator.\
verify_election_consistency(self.election_id)
console_log("### Step complete: verify_election_consistency using `belenios_tool verify` (2)")
# TODO
# console_log("### Starting step: administrator_starts_tallying_of_election")
# self.administrator_starts_tallying_of_election()
# console_log("### Step complete: administrator_starts_tallying_of_election")
console_log("### Starting step: administrator_starts_tallying_of_election")
self.administrator_starts_tallying_of_election(settings.TRUSTEES_THRESHOLD_VALUE)
console_log("### Step complete: administrator_starts_tallying_of_election")
# console_log("### Starting step: trustees_do_partial_decryption")
# self.trustees_do_partial_decryption()
# console_log("### Step complete: trustees_do_partial_decryption")
console_log("### Starting step: trustees_do_partial_decryption")
self.trustees_do_partial_decryption(settings.TRUSTEES_THRESHOLD_VALUE)
console_log("### Step complete: trustees_do_partial_decryption")
# console_log("### Starting step: administrator_finishes_tallying_of_election")
# self.administrator_finishes_tallying_of_election()
# console_log("### Step complete: administrator_finishes_tallying_of_election")
console_log("### Starting step: administrator_finishes_tallying_of_election")
self.administrator_finishes_tallying_of_election(settings.TRUSTEES_THRESHOLD_VALUE)
console_log("### Step complete: administrator_finishes_tallying_of_election")
# console_log("### Starting step: verify_election_consistency using `belenios_tool verify` (3)")
# verify_election_consistency(self.election_id)
# console_log("### Step complete: verify_election_consistency using `belenios_tool verify` (3)")
console_log("### Starting step: verify_election_consistency using `belenios_tool verify` (3)")
verify_election_consistency(self.election_id)
console_log("### Step complete: verify_election_consistency using `belenios_tool verify` (3)")
if __name__ == "__main__":
......
......@@ -160,3 +160,22 @@ def set_element_attribute(browser, element_dom_id, attribute_key, attribute_valu
def verify_element_label(element, expected_label):
element_real_label = element.get_attribute('innerText')
assert expected_label in element_real_label, 'Expected label "' + expected_label + '" not found in element label "' + element_real_label + "'"
def verify_all_elements_have_attribute_value(browser, elements_css_selector, attribute_name, attribute_value):
elements = wait_for_elements_exist(browser, elements_css_selector)
assert len(elements) > 0, "Error: could not find any element in page matching this CSS selector"
for element in elements:
assert element.get_attribute(attribute_name) == attribute_value, "Error: One of the elements corresponding to this CSS selector has a value of '" + element.get_attribute(attribute_name) + "' instead of expected '" + attribute_value + "'"
def verify_some_elements_have_attribute_value(browser, elements_css_selector, attribute_name, attribute_value, necessary_elements):
elements = wait_for_elements_exist(browser, elements_css_selector)
assert len(elements) > 0, "Error: could not find any element in page matching this CSS selector"
elements_matching_condition = 0
for element in elements:
if element.get_attribute(attribute_name) == attribute_value:
elements_matching_condition += 1
if elements_matching_condition >= necessary_elements:
break
assert elements_matching_condition >= necessary_elements, "Error: Not enough elements corresponding to this CSS selector have a value of '" + attribute_value + "' (" + str(elements_matching_condition) + " instead of expected minimum of " + str(necessary_elements) + ")"
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