Beispiel #1
0
 def verify_presence_of_expected_ballot_tracker(self,
                                                expected_ballot_tracker):
     all_smart_ballot_trackers_css_selector = "#main ul li a"
     all_smart_ballot_trackers_elements = wait_for_elements_exist(
         self.browser, all_smart_ballot_trackers_css_selector, self.timeout)
     assert len(all_smart_ballot_trackers_elements)
     matches = [
         element for element in all_smart_ballot_trackers_elements
         if element.get_attribute('innerText') == expected_ballot_tracker
     ]
     assert len(matches) == 1
Beispiel #2
0
    def administrator_finishes_tallying_of_election(self):
        self.browser = initialize_browser_for_scenario_2()
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She checks that encrypted tally hash is still the same as the first time it has been displayed to her
        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)
        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"
        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 + "'"

        # She clicks on the "Compute the result" button
        compute_result_button_expected_label = "Compute the result"
        compute_result_button_css_selector = "#main input[type=submit][value='" + compute_result_button_expected_label + "']"
        compute_result_button_element = wait_for_element_exists(
            browser, compute_result_button_css_selector)
        compute_result_button_element.click()

        wait_a_bit()

        self.administrator_verifies_vote_results()
Beispiel #3
0
    def some_voters_cast_their_vote(self, voters):
        """
        :param voters: list of dict. Each element contains information about a voter (their e-mail address, the planned answers to each question they will cast)
        """
        browser = self.browser
        voters_count = len(voters)
        for index, voter in enumerate(voters):
            console_log("#### Current voter casting their vote in current batch: " + str(index + 1) + "/" + str(voters_count))

            self.one_voter_votes(voter)
            wait_a_bit()

            """
            Next screen looks like this:
            Your ballot for My test election for Scenario 1 has been accepted. Your smart ballot tracker is ISXe/rCNCVa9XcVeFgKglbpgo5SoZs4svT6dPbR5b6M. You can check its presence in the {ballot box} anytime during the election. A confirmation e-mail has been sent to you.

            {Go back to election}

            Where {xxx} is a link
            """

            # He clicks on the "ballot box" link
            ballot_box_link_label = "ballot box"
            ballot_box_link_element = wait_for_an_element_with_partial_link_text_exists(browser, ballot_box_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
            ballot_box_link_element.click()

            wait_a_bit()

            # He checks that his smart ballot tracker appears in the list
            all_smart_ballot_trackers_css_selector = "#main ul li a"
            all_smart_ballot_trackers_elements = wait_for_elements_exist(browser, all_smart_ballot_trackers_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            assert len(all_smart_ballot_trackers_elements)
            matches = [element for element in all_smart_ballot_trackers_elements if element.get_attribute('innerText') == voter["smart_ballot_tracker"]]
            assert len(matches) is 1


            self.voters_email_addresses_who_have_voted[voter["email_address"]] = True

            # In a following pass, he checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted. This verification is done in a separated pass because of an optimization, so that we only re-read and re-populate the sendmail_fake text file once for all users.

            # He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
            # It is not really mandatory for the test to close the window. Re-opening a browser takes much more time, compared to just navigating to another URL. So actually to save execution time, we choose to close the window only sometimes, randomly.
            if random.randint(0, 10) <= 3:
                browser.quit()
                self.browser = initialize_browser()
                browser = self.browser

        # Start another pass, where we re-read and re-populate the sendmail_fake text file once for all users.
        voters = repopulate_vote_confirmations_for_voters_from_sent_emails(self.fake_sent_emails_manager, voters, settings.ELECTION_TITLE)
        for voter in voters:
            # He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
            assert voter["smart_ballot_tracker"] == voter["smart_ballot_tracker_in_vote_confirmation_email"], "Ballot tracker read in vote confirmation email (" + voter["smart_ballot_tracker"] + ") is not the same as the one read on the vote confirmation page (" + voter["smart_ballot_tracker_in_vote_confirmation_email"] + ")"
Beispiel #4
0
    def administrator_finishes_tallying_of_election(self):
        self.browser = initialize_browser_for_scenario_2()
        browser = self.browser

        # Alice goes to the election page
        election_url = self.election_page_url
        browser.get(election_url)

        wait_a_bit()

        # She clicks on the "Administer this election" link
        administration_link_label = "Administer this election"
        administration_link_element = wait_for_an_element_with_partial_link_text_exists(browser, administration_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        administration_link_element.click()

        # She logs in as administrator
        log_in_as_administrator(browser, from_a_login_page=True)

        wait_a_bit()

        # She checks that encrypted tally hash is still the same as the first time it has been displayed to her
        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)
        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"
        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 + "'"

        # She clicks on the "Compute the result" button
        compute_result_button_expected_label = "Compute the result"
        compute_result_button_css_selector = "#main input[type=submit][value='" + compute_result_button_expected_label + "']"
        compute_result_button_element = wait_for_element_exists(browser, compute_result_button_css_selector)
        compute_result_button_element.click()

        wait_a_bit()

        self.administrator_verifies_vote_results()
Beispiel #5
0
    def fill_vote_form(self, vote_data):
        """
        Parameter `vote_data` is a dict with the following structure:
        ```
        {
            "question1": {
                "answer1": False,
                "answer2": True,
            }
        }
        ```
        For now, only one question is supported, with only 2 possible answers.
        """

        answers_elements = wait_for_elements_exist(
            self.browser, self.answers_css_selector, self.timeout
        )  # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]")

        assert len(answers_elements) == 2
        question1_answer1_element = answers_elements[0]
        question1_answer2_element = answers_elements[1]
        voter_vote_to_question_1_answer_1 = vote_data["question1"]["answer1"]
        voter_vote_to_question_1_answer_2 = vote_data["question1"]["answer2"]
        if question1_answer1_element.get_attribute('type') == 'checkbox':
            voter_vote_to_question_1_answer_1_is_checked = question1_answer1_element.is_selected(
            )
            voter_vote_to_question_1_answer_2_is_checked = question1_answer2_element.is_selected(
            )
            if voter_vote_to_question_1_answer_1 and not voter_vote_to_question_1_answer_1_is_checked:
                question1_answer1_element.click()
            if not voter_vote_to_question_1_answer_1 and voter_vote_to_question_1_answer_1_is_checked:
                question1_answer1_element.click()
            if voter_vote_to_question_1_answer_2 and not voter_vote_to_question_1_answer_2_is_checked:
                question1_answer2_element.click()
            if not voter_vote_to_question_1_answer_2 and voter_vote_to_question_1_answer_2_is_checked:
                question1_answer2_element.click()
        else:
            if voter_vote_to_question_1_answer_1:
                question1_answer1_element.send_keys("1")
            if voter_vote_to_question_1_answer_2:
                question1_answer2_element.send_keys("1")
    def administrator_verifies_vote_results(self):
        """
        Initial browser (required) state: on the vote results page
        Final browser state: on the accepted ballots page

        She checks consistency of the vote result:
        - 1) She checks that the number of accepted ballots is the same as the number of voters who voted
        - 2) For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance
        - 3) She checks that each ballot content corresponds to content that of this vote that has been randomly generated in advance


        This screen looks like this:

        This is the development version!
        By using this site, you accept our <personal data policy>. <Accept>
        <en> <fr> <de> <ro> <it>

        This election has been tallied.

            Question 1?
            Answer 1    6
            Answer 2    8

        Number of accepted ballots: 10
        You can also download the <result with cryptographic proofs>.

        <See accepted ballots>

        Where <...> is a link
        """

        browser = self.browser

        # - 1) She checks that the number of accepted ballots is the same as the number of voters who voted

        main_css_selector = "#main"
        main_expected_content = "Number of accepted ballots:"
        main_element = wait_for_element_exists_and_contains_expected_text(
            browser, main_css_selector, main_expected_content,
            settings.EXPLICIT_WAIT_TIMEOUT)
        main_text_content = main_element.get_attribute('innerText')

        number_of_accepted_ballots = None
        match = re.search(r'Number of accepted ballots:\s*(\d+)\s',
                          main_text_content, re.MULTILINE | re.DOTALL)
        if match:
            number_of_accepted_ballots = match.group(1)
            number_of_accepted_ballots = number_of_accepted_ballots.strip()
        else:
            raise Exception(
                "Number of accepted ballots not found in election tally page: "
                + main_text_content)
        assert str(number_of_accepted_ballots) == str(
            settings.NUMBER_OF_VOTING_VOTERS
        ), "Number of accepted ballots (" + str(
            number_of_accepted_ballots
        ) + ") is not the same as number of voters (" + str(
            settings.NUMBER_OF_VOTING_VOTERS) + ")" + printable_page_source(
                browser)

        # - 2) For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance

        number_of_votes_per_answer = self.compute_number_of_votes_per_answer()
        question_id = 1
        for answer_id in range(1, 3):
            base_selector = "#main li:nth-child(" + str(
                question_id) + ") tr:nth-child(" + str(answer_id) + ")"
            answer_label_css_selector = base_selector + " td:nth-child(1)"
            answer_total_css_selector = base_selector + " td:nth-child(2)"
            answer_expected_label = "Answer " + str(answer_id)
            answer_element = browser.find_element_by_css_selector(
                answer_label_css_selector)
            verify_element_label(answer_element, answer_expected_label)
            answer_total_real_value_element = browser.find_element_by_css_selector(
                answer_total_css_selector)
            answer_total_real_value = answer_total_real_value_element.get_attribute(
                'innerText').strip()
            answer_total_expected_value = str(
                number_of_votes_per_answer['answer' + str(answer_id)])
            assert answer_total_real_value == answer_total_expected_value, "Number of votes for Answer " + str(
                answer_id
            ) + " displayed on vote result page  (" + answer_total_real_value + ") does not match expected value (" + answer_total_expected_value + "). " + printable_page_source(
                browser)

        # - 3) She checks that each smart ballot tracker in the ballot box page corresponds to the smart ballot tracker of one of our voters, and that there is only one of these, and that the number of smart ballot trackers in this page is the same as the number of voters who voted

        all_ballots_link_label = "See accepted ballots"
        all_ballots_link_element = wait_for_an_element_with_partial_link_text_exists(
            browser, all_ballots_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        all_ballots_link_element.click()

        all_smart_ballot_trackers_css_selector = "#main ul li a"
        all_smart_ballot_trackers_elements = wait_for_elements_exist(
            browser, all_smart_ballot_trackers_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(self.voters_email_addresses_who_have_voted
                   ) == settings.NUMBER_OF_VOTING_VOTERS
        assert len(all_smart_ballot_trackers_elements
                   ) == settings.NUMBER_OF_VOTING_VOTERS
        for voter_email_address in self.voters_email_addresses_who_have_voted:
            voter = self.voters_data[voter_email_address]
            matches = [
                element for element in all_smart_ballot_trackers_elements
                if element.get_attribute('innerText') ==
                voter["smart_ballot_tracker"]
            ]
            assert len(matches) is 1
    def administrator_regenerates_passwords_for_some_voters(self):
        # Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
        browser = self.browser
        log_in_as_administrator(browser)

        # She remembers the list of voters who contacted her and said they lost their password. For this, we pick randomly NUMBER_OF_REGENERATED_PASSWORD_VOTERS voters from all the voters.
        self.voters_email_addresses_who_have_lost_their_password = random.sample(
            self.voters_email_addresses,
            settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS)

        # She selects the election that she wants to edit
        browser = self.browser
        election_to_edit_css_selector = "#election_admin_" + str(
            self.election_id)
        election_to_edit_elements = wait_for_elements_exist(
            browser, election_to_edit_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(election_to_edit_elements) > 0
        election_to_edit_elements[0].click()

        wait_a_bit()

        # She arrives to the election administration page. For each voter of the NUMBER_OF_REGENERATED_PASSWORD_VOTERS selected voters:
        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            # She clicks on the "Regenerate and mail a password" link
            regenerate_and_mail_a_password_link_css_selector = "#election_regenpwd"
            regenerate_and_mail_a_password_link_element = wait_for_element_exists(
                browser, regenerate_and_mail_a_password_link_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            regenerate_and_mail_a_password_link_element.click()

            wait_a_bit()

            # She types the e-mail address of the voter in the "Username" field
            username_field_css_selector = "#main input[type=text]"
            username_field_element = wait_for_element_exists(
                browser, username_field_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            username_field_element.send_keys(email_address)

            wait_a_bit()

            # She clicks on the "Submit" button
            submit_button_label = "Submit"
            submit_button_element = find_button_in_page_content_by_value(
                browser, submit_button_label)
            submit_button_element.click()

            wait_a_bit()

            # She checks that the page shows a confirmation message similar to "A new password has been mailed to [email protected]"
            confirmation_sentence_expected_text = "A new password has been mailed to"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(
                browser, confirmation_sentence_css_selector,
                confirmation_sentence_expected_text,
                settings.EXPLICIT_WAIT_TIMEOUT)

            # She clicks on the "Proceed" link
            proceed_link_css_selector = "#generic_proceed_link"
            proceed_link_element = wait_for_element_exists(
                browser, proceed_link_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            proceed_link_element.click()

            wait_a_bit()

            # She arrives back to the election administration page
        """
        Now we do a sanity check that server has really tried to send these emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to. There should be 3 occurences of "To : xxx@xxx" for users who have lost their password, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"

        And there should be only 2 occurences for other users, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        """

        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(
                text_to_look_for) is 3

        voters_email_addresses_who_have_not_lost_their_password = set(
            self.voters_email_addresses) - set(
                self.voters_email_addresses_who_have_lost_their_password)

        for email_address in voters_email_addresses_who_have_not_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(
                text_to_look_for) is 2

        log_out(browser, self.election_id)
Beispiel #8
0
 def verify_page_body(self):
     answers_elements = wait_for_elements_exist(self.browser, self.answers_css_selector, self.timeout)
     assert len(answers_elements) == 2
Beispiel #9
0
    def some_voters_cast_their_vote(self, voters):
        """
        :param voters: list of dict. Each element contains information about a voter (their e-mail address, the planned answers to each question they will cast)
        """
        browser = self.browser
        voters_count = len(voters)
        for index, voter in enumerate(voters):
            console_log(
                "#### Current voter casting their vote in current batch: " +
                str(index + 1) + "/" + str(voters_count))

            self.one_voter_votes(voter)
            wait_a_bit()
            """
            Next screen looks like this:
            Your ballot for My test election for Scenario 1 has been accepted. Your smart ballot tracker is ISXe/rCNCVa9XcVeFgKglbpgo5SoZs4svT6dPbR5b6M. You can check its presence in the {ballot box} anytime during the election. A confirmation e-mail has been sent to you.

            {Go back to election}

            Where {xxx} is a link
            """

            # He clicks on the "ballot box" link
            ballot_box_link_label = "ballot box"
            ballot_box_link_element = wait_for_an_element_with_partial_link_text_exists(
                browser, ballot_box_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
            ballot_box_link_element.click()

            wait_a_bit()

            # He checks that his smart ballot tracker appears in the list
            all_smart_ballot_trackers_css_selector = "#main ul li a"
            all_smart_ballot_trackers_elements = wait_for_elements_exist(
                browser, all_smart_ballot_trackers_css_selector,
                settings.EXPLICIT_WAIT_TIMEOUT)
            assert len(all_smart_ballot_trackers_elements)
            matches = [
                element for element in all_smart_ballot_trackers_elements
                if element.get_attribute('innerText') ==
                voter["smart_ballot_tracker"]
            ]
            assert len(matches) is 1

            self.voters_email_addresses_who_have_voted[
                voter["email_address"]] = True

            # In a following pass, he checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted. This verification is done in a separated pass because of an optimization, so that we only re-read and re-populate the sendmail_fake text file once for all users.

            # He closes the window (there is no log-out link, because user is not logged in: credentials are not remembered)
            # It is not really mandatory for the test to close the window. Re-opening a browser takes much more time, compared to just navigating to another URL. So actually to save execution time, we choose to close the window only sometimes, randomly.
            if random.randint(0, 10) <= 3:
                browser.quit()
                self.browser = initialize_browser()
                browser = self.browser

        # Start another pass, where we re-read and re-populate the sendmail_fake text file once for all users.
        voters = repopulate_vote_confirmations_for_voters_from_sent_emails(
            self.fake_sent_emails_manager, voters, settings.ELECTION_TITLE)
        for voter in voters:
            # He checks his mailbox to find a new email with confirmation of his vote, and verifies the value of the smart ballot tracker written in this email is the same as the one he noted.
            assert voter["smart_ballot_tracker"] == voter[
                "smart_ballot_tracker_in_vote_confirmation_email"], "Ballot tracker read in vote confirmation email (" + voter[
                    "smart_ballot_tracker"] + ") is not the same as the one read on the vote confirmation page (" + voter[
                        "smart_ballot_tracker_in_vote_confirmation_email"] + ")"
Beispiel #10
0
    def one_voter_votes(self, voter, direct=False):
        browser = self.browser

        if direct:
            url = voter["election_page_url"]
            browser.get(settings.SERVER_URL + "/vote.html#" +
                        urlencode({"url": url}))

        else:
            # 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"])

            wait_a_bit()

            # He clicks on the "Start" button
            start_button_expected_label = "Start"
            start_button_css_selector = "#main button"
            start_button_element = wait_for_element_exists_and_contains_expected_text(
                browser, start_button_css_selector,
                start_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
            start_button_element.click()

        wait_a_bit()

        # A loading screen appears, then another screen appears. He clicks on the "Here" button
        here_button_expected_label = "here"
        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,
            settings.EXPLICIT_WAIT_TIMEOUT)
        here_button_element.click()

        wait_a_bit()

        # 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.send_keys(voter["credential"])
        credential_prompt.accept()

        wait_a_bit()

        # A new screen appears, which has a title "Step 2/6: Answer to questions", and a content:
        # "Question 1?"
        # "Question #1 of 1 — select between 1 and 2 answer(s)"
        # [ ] "Answer 1"
        # [ ] "Answer 2"
        # [Next]
        # (where "[ ]" is a checkbox, and [Next] is a button)

        # He fills his votes to each answer of the question
        answers_css_selector = "#question_div input[type=checkbox]"
        answers_elements = wait_for_elements_exist(
            browser, answers_css_selector, settings.EXPLICIT_WAIT_TIMEOUT
        )  # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]")

        assert len(answers_elements) is 2
        question1_answer1_element = answers_elements[0]
        question1_answer2_element = answers_elements[1]
        voter_vote_to_question_1_answer_1 = voter["votes"]["question1"][
            "answer1"]
        voter_vote_to_question_1_answer_2 = voter["votes"]["question1"][
            "answer2"]
        voter_vote_to_question_1_answer_1_is_checked = question1_answer1_element.get_attribute(
            'checked')
        voter_vote_to_question_1_answer_2_is_checked = question1_answer2_element.get_attribute(
            'checked')
        assert voter_vote_to_question_1_answer_1_is_checked is None
        assert voter_vote_to_question_1_answer_2_is_checked is None
        if voter_vote_to_question_1_answer_1:
            question1_answer1_element.click()
        if voter_vote_to_question_1_answer_2:
            question1_answer2_element.click()

        wait_a_bit()

        # He clicks on the "Next" button
        next_button_expected_label = "Next"
        next_button_css_selector = "#question_div button"
        next_button_element = wait_for_element_exists_and_contains_expected_text(
            browser, next_button_css_selector, next_button_expected_label,
            settings.EXPLICIT_WAIT_TIMEOUT)
        next_button_element.click()

        wait_a_bit()
        """
        A new screen appears, showing:

        Step 3/6: Review and encrypt
        Question 1?
        - Answer 1

        Your ballot has been successfully encrypted, but has not been cast yet!

        Your smart ballot tracker is sLRilXoAYcodIrjWrOqPrVXLNlRyCJAqFeeHZ4WCajU

        We invite you to save it in order to check later that it is taken into account.

        [Continue]
        [Restart]
        """

        # He remembers the smart ballot tracker that is displayed.
        smart_ballot_tracker_css_selector = "#ballot_tracker"
        smart_ballot_tracker_element = wait_for_element_exists_and_has_non_empty_content(
            browser, smart_ballot_tracker_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute(
            'innerText')
        assert len(smart_ballot_tracker_value) > 5

        voter["smart_ballot_tracker"] = smart_ballot_tracker_value

        # He clicks on the "Continue" button
        next_button_expected_label = "Continue"
        next_button_css_selector = "#div_submit input[type=submit][value='" + next_button_expected_label + "']"
        next_button_element = browser.find_element_by_css_selector(
            next_button_css_selector)
        next_button_element.click()

        wait_a_bit()

        # He types his voter username and password, and submits the form
        username_field_css_selector = "#main input[name=username]"
        username_field_element = wait_for_element_exists(
            browser, username_field_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        username_field_element.send_keys(voter["username"])

        password_field_css_selector = "#main input[name=password]"
        password_field_element = browser.find_element_by_css_selector(
            password_field_css_selector)
        password_field_element.send_keys(voter["password"])

        wait_a_bit()

        password_field_element.submit()

        wait_a_bit()

        # 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_selector = "#ballot_tracker"
        smart_ballot_tracker_verification_element = wait_for_element_exists_and_has_non_empty_content(
            browser, smart_ballot_tracker_verification_css_selector,
            settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_verification_value = smart_ballot_tracker_verification_element.get_attribute(
            'innerText')
        assert len(smart_ballot_tracker_verification_value) > 5

        assert smart_ballot_tracker_verification_value == voter[
            "smart_ballot_tracker"]

        # He clicks on the "I cast my vote" button
        submit_button_css_selector = "#main input[type=submit]"
        submit_button_element = browser.find_element_by_css_selector(
            submit_button_css_selector)
        submit_button_element.click()
Beispiel #11
0
    def administrator_verifies_vote_results(self):
        """
        Initial browser (required) state: on the vote results page
        Final browser state: on the accepted ballots page

        She checks consistency of the vote result:
        - 1) She checks that the number of accepted ballots is the same as the number of voters who voted
        - 2) For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance
        - 3) She checks that each ballot content corresponds to content that of this vote that has been randomly generated in advance


        This screen looks like this:

        This is the development version!
        By using this site, you accept our <personal data policy>. <Accept>
        <en> <fr> <de> <ro> <it>

        This election has been tallied.

            Question 1?
            Answer 1    6
            Answer 2    8

        Number of accepted ballots: 10
        You can also download the <result with cryptographic proofs>.

        <See accepted ballots>

        Where <...> is a link
        """

        browser = self.browser

        # - 1) She checks that the number of accepted ballots is the same as the number of voters who voted

        main_css_selector = "#main"
        main_expected_content = "Number of accepted ballots:"
        main_element = wait_for_element_exists_and_contains_expected_text(browser, main_css_selector, main_expected_content, settings.EXPLICIT_WAIT_TIMEOUT)
        main_text_content = main_element.get_attribute('innerText')

        number_of_accepted_ballots = None
        match = re.search(r'Number of accepted ballots:\s*(\d+)\s', main_text_content, re.MULTILINE | re.DOTALL)
        if match:
            number_of_accepted_ballots = match.group(1)
            number_of_accepted_ballots = number_of_accepted_ballots.strip()
        else:
            raise Exception("Number of accepted ballots not found in election tally page: " + main_text_content)
        assert str(number_of_accepted_ballots) == str(settings.NUMBER_OF_VOTING_VOTERS), "Number of accepted ballots (" + str(number_of_accepted_ballots) + ") is not the same as number of voters (" + str(settings.NUMBER_OF_VOTING_VOTERS) + ")"


        # - 2) For each available answer in the question, she checks that the total number of votes in favor of Answer X displayed in result page is the same as the sum of votes for Answer X in all votes of voters who voted that have been randomly generated in advance

        number_of_votes_per_answer = self.compute_number_of_votes_per_answer()
        question_id = 1
        for answer_id in range(1, 3):
            base_selector = "#main li:nth-child(" + str(question_id) + ") tr:nth-child(" + str(answer_id) + ")"
            answer_label_css_selector = base_selector + " td:nth-child(1)"
            answer_total_css_selector = base_selector + " td:nth-child(2)"
            answer_expected_label = "Answer " + str(answer_id)
            answer_element = browser.find_element_by_css_selector(answer_label_css_selector)
            verify_element_label(answer_element, answer_expected_label)
            answer_total_real_value_element = browser.find_element_by_css_selector(answer_total_css_selector)
            answer_total_real_value = answer_total_real_value_element.get_attribute('innerText').strip()
            answer_total_expected_value = str(number_of_votes_per_answer['answer' + str(answer_id)])
            assert answer_total_real_value == answer_total_expected_value, "Number of votes for Answer " + str(answer_id) + " displayed on vote result page  (" + answer_total_real_value + ") does not match expected value (" + answer_total_expected_value


        # - 3) She checks that each smart ballot tracker in the ballot box page corresponds to the smart ballot tracker of one of our voters, and that there is only one of these, and that the number of smart ballot trackers in this page is the same as the number of voters who voted

        all_ballots_link_label = "See accepted ballots"
        all_ballots_link_element = wait_for_an_element_with_partial_link_text_exists(browser, all_ballots_link_label, settings.EXPLICIT_WAIT_TIMEOUT)
        all_ballots_link_element.click()

        all_smart_ballot_trackers_css_selector = "#main ul li a"
        all_smart_ballot_trackers_elements = wait_for_elements_exist(browser, all_smart_ballot_trackers_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(self.voters_email_addresses_who_have_voted) == settings.NUMBER_OF_VOTING_VOTERS
        assert len(all_smart_ballot_trackers_elements) == settings.NUMBER_OF_VOTING_VOTERS
        for voter_email_address in self.voters_email_addresses_who_have_voted:
            voter = self.voters_data[voter_email_address]
            matches = [element for element in all_smart_ballot_trackers_elements if element.get_attribute('innerText') == voter["smart_ballot_tracker"]]
            assert len(matches) is 1
Beispiel #12
0
    def one_voter_votes(self, voter, direct=False):
        browser = self.browser

        if direct:
            url = voter["election_page_url"]
            browser.get(settings.SERVER_URL + "/vote.html#" + urlencode({"url": url}))

        else:
            # 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"])

            wait_a_bit()

            # He clicks on the "Start" button
            start_button_expected_label = "Start"
            start_button_css_selector = "#main button"
            start_button_element = wait_for_element_exists_and_contains_expected_text(browser, start_button_css_selector, start_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
            start_button_element.click()

        wait_a_bit()

        # A loading screen appears, then another screen appears. He clicks on the "Here" button
        here_button_expected_label = "here"
        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, settings.EXPLICIT_WAIT_TIMEOUT)
        here_button_element.click()

        wait_a_bit()

        # 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.send_keys(voter["credential"])
        credential_prompt.accept()

        wait_a_bit()

        # A new screen appears, which has a title "Step 2/6: Answer to questions", and a content:
        # "Question 1?"
        # "Question #1 of 1 — select between 1 and 2 answer(s)"
        # [ ] "Answer 1"
        # [ ] "Answer 2"
        # [Next]
        # (where "[ ]" is a checkbox, and [Next] is a button)

        # He fills his votes to each answer of the question
        answers_css_selector = "#question_div input[type=checkbox]"
        answers_elements = wait_for_elements_exist(browser, answers_css_selector, settings.EXPLICIT_WAIT_TIMEOUT) # or we could use find_element_by_xpath("//div[@id='question_div']/input[@type='checkbox'][2]")

        assert len(answers_elements) is 2
        question1_answer1_element = answers_elements[0]
        question1_answer2_element = answers_elements[1]
        voter_vote_to_question_1_answer_1 = voter["votes"]["question1"]["answer1"]
        voter_vote_to_question_1_answer_2 = voter["votes"]["question1"]["answer2"]
        voter_vote_to_question_1_answer_1_is_checked = question1_answer1_element.get_attribute('checked')
        voter_vote_to_question_1_answer_2_is_checked = question1_answer2_element.get_attribute('checked')
        assert voter_vote_to_question_1_answer_1_is_checked is None
        assert voter_vote_to_question_1_answer_2_is_checked is None
        if voter_vote_to_question_1_answer_1:
            question1_answer1_element.click()
        if voter_vote_to_question_1_answer_2:
            question1_answer2_element.click()

        wait_a_bit()

        # He clicks on the "Next" button
        next_button_expected_label = "Next"
        next_button_css_selector = "#question_div button"
        next_button_element = wait_for_element_exists_and_contains_expected_text(browser, next_button_css_selector, next_button_expected_label, settings.EXPLICIT_WAIT_TIMEOUT)
        next_button_element.click()

        wait_a_bit()

        """
        A new screen appears, showing:

        Step 3/6: Review and encrypt
        Question 1?
        - Answer 1

        Your ballot has been successfully encrypted, but has not been cast yet!

        Your smart ballot tracker is sLRilXoAYcodIrjWrOqPrVXLNlRyCJAqFeeHZ4WCajU

        We invite you to save it in order to check later that it is taken into account.

        [Continue]
        [Restart]
        """

        # He remembers the smart ballot tracker that is displayed.
        smart_ballot_tracker_css_selector = "#ballot_tracker"
        smart_ballot_tracker_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_value = smart_ballot_tracker_element.get_attribute('innerText')
        assert len(smart_ballot_tracker_value) > 5

        voter["smart_ballot_tracker"] = smart_ballot_tracker_value

        # He clicks on the "Continue" button
        next_button_expected_label = "Continue"
        next_button_css_selector = "#div_submit input[type=submit][value='" + next_button_expected_label + "']"
        next_button_element = browser.find_element_by_css_selector(next_button_css_selector)
        next_button_element.click()

        wait_a_bit()

        # He types his voter username and password, and submits the form
        username_field_css_selector = "#main input[name=username]"
        username_field_element = wait_for_element_exists(browser, username_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        username_field_element.send_keys(voter["username"])

        password_field_css_selector = "#main input[name=password]"
        password_field_element = browser.find_element_by_css_selector(password_field_css_selector)
        password_field_element.send_keys(voter["password"])

        wait_a_bit()

        password_field_element.submit()

        wait_a_bit()

        # 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_selector = "#ballot_tracker"
        smart_ballot_tracker_verification_element = wait_for_element_exists_and_has_non_empty_content(browser, smart_ballot_tracker_verification_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        smart_ballot_tracker_verification_value = smart_ballot_tracker_verification_element.get_attribute('innerText')
        assert len(smart_ballot_tracker_verification_value) > 5

        assert smart_ballot_tracker_verification_value == voter["smart_ballot_tracker"]

        # He clicks on the "I cast my vote" button
        submit_button_css_selector = "#main input[type=submit]"
        submit_button_element = browser.find_element_by_css_selector(submit_button_css_selector)
        submit_button_element.click()
Beispiel #13
0
    def administrator_regenerates_passwords_for_some_voters(self):
        # Alice has been contacted by some voters who say they lost their password. She wants to re-generate their passwords and have the platform send them by email. For this, she logs in as administrator.
        browser = self.browser
        log_in_as_administrator(browser)


        # She remembers the list of voters who contacted her and said they lost their password. For this, we pick randomly NUMBER_OF_REGENERATED_PASSWORD_VOTERS voters from all the voters.
        self.voters_email_addresses_who_have_lost_their_password = random.sample(self.voters_email_addresses, settings.NUMBER_OF_REGENERATED_PASSWORD_VOTERS)

        # She selects the election that she wants to edit
        browser = self.browser
        election_to_edit_css_selector = "#election_admin_" + str(self.election_id)
        election_to_edit_elements = wait_for_elements_exist(browser, election_to_edit_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
        assert len(election_to_edit_elements) > 0
        election_to_edit_elements[0].click()

        wait_a_bit()

        # She arrives to the election administration page. For each voter of the NUMBER_OF_REGENERATED_PASSWORD_VOTERS selected voters:
        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            # She clicks on the "Regenerate and mail a password" link
            regenerate_and_mail_a_password_link_css_selector = "#election_regenpwd"
            regenerate_and_mail_a_password_link_element = wait_for_element_exists(browser, regenerate_and_mail_a_password_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            regenerate_and_mail_a_password_link_element.click()

            wait_a_bit()

            # She types the e-mail address of the voter in the "Username" field
            username_field_css_selector = "#main input[type=text]"
            username_field_element = wait_for_element_exists(browser, username_field_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            username_field_element.send_keys(email_address)

            wait_a_bit()

            # She clicks on the "Submit" button
            submit_button_label = "Submit"
            submit_button_element = find_button_in_page_content_by_value(browser, submit_button_label)
            submit_button_element.click()

            wait_a_bit()

            # She checks that the page shows a confirmation message similar to "A new password has been mailed to [email protected]"
            confirmation_sentence_expected_text = "A new password has been mailed to"
            confirmation_sentence_css_selector = "#main p"
            wait_for_element_exists_and_contains_expected_text(browser, confirmation_sentence_css_selector, confirmation_sentence_expected_text, settings.EXPLICIT_WAIT_TIMEOUT)

            # She clicks on the "Proceed" link
            proceed_link_css_selector = "#generic_proceed_link"
            proceed_link_element = wait_for_element_exists(browser, proceed_link_css_selector, settings.EXPLICIT_WAIT_TIMEOUT)
            proceed_link_element.click()

            wait_a_bit()

            # She arrives back to the election administration page

        """
        Now we do a sanity check that server has really tried to send these emails. For this, we look for email addresses in the temporary file where our fake sendmail executable redirects its inputs to. There should be 3 occurences of "To : xxx@xxx" for users who have lost their password, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"

        And there should be only 2 occurences for other users, with respective subjects:
        - "Your credential for election My test election for Scenario 1"
        - "Your password for election My test election for Scenario 1"
        """

        for email_address in self.voters_email_addresses_who_have_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(text_to_look_for) is 3

        voters_email_addresses_who_have_not_lost_their_password = set(self.voters_email_addresses) - set(self.voters_email_addresses_who_have_lost_their_password)

        for email_address in voters_email_addresses_who_have_not_lost_their_password:
            text_to_look_for = 'To: "' + email_address + '"'
            assert self.fake_sent_emails_manager.count_occurences_in_sent_emails(text_to_look_for) is 2

        log_out(browser)