pay_and_verify.py 6.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
"""Payment and verification pages"""

import re
from urllib import urlencode

from bok_choy.page_object import PageObject, unguarded
from bok_choy.promise import Promise, EmptyPromise
from . import BASE_URL
from .dashboard import DashboardPage


class PaymentAndVerificationFlow(PageObject):
    """Interact with the split payment and verification flow.

    These pages are currently hidden behind the feature flag
    `SEPARATE_VERIFICATION_FROM_PAYMENT`, which is enabled in
    the Bok Choy settings.

    When enabled, the flow can be accessed at the following URLs:
        `/verify_student/start-flow/{course}/`
        `/verify_student/upgrade/{course}/`
        `/verify_student/verify-now/{course}/`
        `/verify_student/verify-later/{course}/`
        `/verify_student/payment-confirmation/{course}/`

    Users can reach the flow when attempting to enroll in a course's verified
    mode, either directly from the track selection page, or by upgrading from
    the honor mode. Users can also reach the flow when attempting to complete
    a deferred verification, or when attempting to view a receipt corresponding
    to an earlier payment.
    """
    def __init__(self, browser, course_id, entry_point='start-flow'):
        """Initialize the page.

        Arguments:
            browser (Browser): The browser instance.
            course_id (unicode): The course in which the user is enrolling.

        Keyword Arguments:
            entry_point (str): Where to begin the flow; must be one of 'start-flow',
                'upgrade', 'verify-now', verify-later', or 'payment-confirmation'.

        Raises:
            ValueError
        """
        super(PaymentAndVerificationFlow, self).__init__(browser)
        self._course_id = course_id

        if entry_point not in ['start-flow', 'upgrade', 'verify-now', 'verify-later', 'payment-confirmation']:
            raise ValueError(
                "Entry point must be either 'start-flow', 'upgrade', 'verify-now', 'verify-later', or 'payment-confirmation'."
            )
        self._entry_point = entry_point

    @property
    def url(self):
        """Return the URL corresponding to the initial position in the flow."""
        url = "{base}/verify_student/{entry_point}/{course}".format(
            base=BASE_URL,
            entry_point=self._entry_point,
            course=self._course_id
        )

        return url

    def is_browser_on_page(self):
        """Check if a step in the payment and verification flow has loaded."""
        return (
            self.q(css="div .make-payment-step").is_present() or
            self.q(css="div .payment-confirmation-step").is_present() or
            self.q(css="div .face-photo-step").is_present() or
            self.q(css="div .id-photo-step").is_present() or
            self.q(css="div .review-photos-step").is_present() or
            self.q(css="div .enrollment-confirmation-step").is_present()
        )

    def indicate_contribution(self):
        """Interact with the radio buttons appearing on the first page of the upgrade flow."""
        self.q(css=".contribution-option > input").first.click()

    def proceed_to_payment(self):
        """Interact with the payment button."""
        self.q(css="#pay_button").click()

        FakePaymentPage(self.browser, self._course_id).wait_for_page()

    def immediate_verification(self):
        """Interact with the immediate verification button."""
        self.q(css="#verify_now_button").click()

        PaymentAndVerificationFlow(self.browser, self._course_id, entry_point='verify-now').wait_for_page()

    def defer_verification(self):
        """Interact with the link allowing the user to defer their verification."""
        self.q(css="#verify_later_button").click()

        DashboardPage(self.browser).wait_for_page()

    def webcam_capture(self):
        """Interact with a webcam capture button."""
        self.q(css="#webcam_capture_button").click()

        def _check_func():
            next_step_button_classes = self.q(css="#next_step_button").attrs('class')
            next_step_button_enabled = 'is-disabled' not in next_step_button_classes
            return (next_step_button_enabled, next_step_button_classes)

        # Check that the #next_step_button is enabled before returning control to the caller
        Promise(_check_func, "The 'Next Step' button is enabled.").fulfill()

    def next_verification_step(self, next_page_object):
        """Interact with the 'Next' step button found in the verification flow."""
        self.q(css="#next_step_button").click()

        next_page_object.wait_for_page()

    def go_to_dashboard(self):
        """Interact with the link to the dashboard appearing on the enrollment confirmation page."""
        if self.q(css="div .enrollment-confirmation-step").is_present():
            self.q(css=".action-primary").click()
        else:
            raise Exception("The dashboard can only be accessed from the enrollment confirmation.")

        DashboardPage(self.browser, separate_verified=True).wait_for_page()


class FakePaymentPage(PageObject):
    """Interact with the fake payment endpoint.

    This page is hidden behind the feature flag `ENABLE_PAYMENT_FAKE`,
    which is enabled in the Bok Choy env settings.

    Configuring this payment endpoint also requires configuring the Bok Choy
    auth settings with the following:

        "CC_PROCESSOR_NAME": "CyberSource2",
        "CC_PROCESSOR": {
            "CyberSource2": {
                "SECRET_KEY": <string>,
                "ACCESS_KEY": <string>,
                "PROFILE_ID": "edx",
                "PURCHASE_ENDPOINT": "/shoppingcart/payment_fake"
            }
        }
    """
    def __init__(self, browser, course_id):
        """Initialize the page.

        Arguments:
            browser (Browser): The browser instance.
            course_id (unicode): The course in which the user is enrolling.
        """
        super(FakePaymentPage, self).__init__(browser)
        self._course_id = course_id

    url = BASE_URL + "/shoppingcart/payment_fake/"

    def is_browser_on_page(self):
        """Check if a step in the payment and verification flow has loaded."""
        message = self.q(css='BODY').text[0]
        match = re.search('Payment page', message)
        return True if match else False

    def submit_payment(self):
        """Interact with the payment submission button."""
        self.q(css="input[value='Submit']").click()

        return PaymentAndVerificationFlow(self.browser, self._course_id, entry_point='payment-confirmation').wait_for_page()