Commit 0135e93c by Clinton Blackburn

Fixed checkout acceptance tests

ECOM-7099
parent b10116a8
import requests
from edx_rest_api_client.client import EdxRestApiClient
from requests.auth import AuthBase
from e2e.config import ACCESS_TOKEN, ENROLLMENT_API_URL
from e2e.config import ENROLLMENT_API_URL, OAUTH_ACCESS_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET
def get_access_token():
""" Returns an access token and expiration date from the OAuth provider.
Returns:
(str, datetime)
"""
# TODO Use JWT auth once https://github.com/edx/edx-platform/pull/14577 is merged/released.
return EdxRestApiClient.get_oauth_access_token(
OAUTH_ACCESS_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET,
)
class BearerAuth(AuthBase):
......@@ -18,13 +31,13 @@ class BearerAuth(AuthBase):
class EnrollmentApiClient(object):
def __init__(self, host=None, key=None):
self.host = host or ENROLLMENT_API_URL
self.key = key or ACCESS_TOKEN
def __init__(self):
access_token, __ = get_access_token()
self.client = EdxRestApiClient(ENROLLMENT_API_URL, oauth_access_token=access_token, append_slash=False)
def get_enrollment_status(self, username, course_id):
"""
Retrieve the enrollment status for given user in a given course.
"""
url = '{host}/enrollment/{username},{course_id}'.format(host=self.host, username=username, course_id=course_id)
return requests.get(url, auth=BearerAuth(self.key)).json()
param = '{username},{course_id}'.format(username=username, course_id=course_id)
return self.client.enrollment(param).get()
......@@ -2,9 +2,12 @@ import os
from e2e.utils import str2bool
ACCESS_TOKEN = os.environ.get('ACCESS_TOKEN')
if ACCESS_TOKEN is None:
raise RuntimeError('A valid OAuth2 access token is required.')
OAUTH_ACCESS_TOKEN_URL = os.environ.get('OAUTH_ACCESS_TOKEN_URL')
OAUTH_CLIENT_ID = os.environ.get('OAUTH_CLIENT_ID')
OAUTH_CLIENT_SECRET = os.environ.get('OAUTH_CLIENT_SECRET')
if not all([OAUTH_ACCESS_TOKEN_URL, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET]):
raise RuntimeError('Valid OAuth details must be provided.')
HONOR_COURSE_ID = os.environ.get('HONOR_COURSE_ID')
VERIFIED_COURSE_ID = os.environ.get('VERIFIED_COURSE_ID')
......@@ -20,9 +23,11 @@ except AttributeError:
ECOMMERCE_API_URL = os.environ.get('ECOMMERCE_API_URL', ECOMMERCE_URL_ROOT + '/api/v2')
MAX_COMPLETION_RETRIES = int(os.environ.get('MAX_COMPLETION_RETRIES', 3))
PAYPAL_EMAIL = os.environ.get('PAYPAL_EMAIL')
PAYPAL_PASSWORD = os.environ.get('PAYPAL_PASSWORD')
ENABLE_CYBERSOURCE_TESTS = str2bool(os.environ.get('ENABLE_CYBERSOURCE_TESTS', True))
if not all([PAYPAL_EMAIL, PAYPAL_PASSWORD]):
raise RuntimeError('PayPal credentials are required to fully test payment.')
try:
MARKETING_URL_ROOT = os.environ.get('MARKETING_URL_ROOT').strip('/')
......
......@@ -2,8 +2,7 @@ from datetime import date
CODE = 'ABCD'
# cybersource data
CYBERSOURCE_DATA1 = {
ADDRESS_US = {
'country': 'US',
'state': 'MA',
'line1': '141 Portland Ave.',
......@@ -11,7 +10,7 @@ CYBERSOURCE_DATA1 = {
'city': 'Cambridge',
'postal_code': '02141',
}
CYBERSOURCE_DATA2 = {
ADDRESS_FR = {
'country': 'FR',
'state': None,
'line1': 'Champ de Mars',
......
import logging
import time
import uuid
import requests
......@@ -9,22 +10,11 @@ from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
from e2e.api import EnrollmentApiClient
from e2e.api import EnrollmentApiClient, get_access_token
from e2e.config import (
LMS_AUTO_AUTH,
ECOMMERCE_URL_ROOT,
LMS_PASSWORD,
LMS_EMAIL,
LMS_URL_ROOT,
BASIC_AUTH_USERNAME,
BASIC_AUTH_PASSWORD,
ECOMMERCE_API_URL,
LMS_USERNAME,
ACCESS_TOKEN,
MAX_COMPLETION_RETRIES,
PAYPAL_PASSWORD,
PAYPAL_EMAIL,
LMS_HTTPS
LMS_AUTO_AUTH, ECOMMERCE_URL_ROOT, LMS_PASSWORD, LMS_EMAIL, LMS_URL_ROOT, BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD,
ECOMMERCE_API_URL, LMS_USERNAME, MAX_COMPLETION_RETRIES, PAYPAL_PASSWORD, PAYPAL_EMAIL, LMS_HTTPS,
MARKETING_URL_ROOT
)
from e2e.expected_conditions import input_provided
from e2e.pages import submit_lms_login_form
......@@ -98,7 +88,8 @@ class LMSLogoutMixin(object):
self.lms_logout_page = LMSLogoutPage(self.browser)
def assert_on_lms_logout_page(self):
self.assertTrue(self.lms_logout_page.is_browser_on_page())
lms_homepage_url = MARKETING_URL_ROOT or LMS_URL_ROOT
self.assertTrue(self.browser.current_url.startswith(lms_homepage_url))
def logout_via_lms(self):
self.lms_logout_page.visit()
......@@ -163,7 +154,8 @@ class EnrollmentApiMixin(object):
class EcommerceApiMixin(object):
@property
def ecommerce_api_client(self):
return EdxRestApiClient(ECOMMERCE_API_URL, oauth_access_token=ACCESS_TOKEN)
access_token, __ = get_access_token()
return EdxRestApiClient(ECOMMERCE_API_URL, oauth_access_token=access_token)
def assert_order_created_and_completed(self):
orders = self.ecommerce_api_client.orders.get()['results']
......@@ -223,20 +215,28 @@ class PaymentMixin(object):
except TimeoutException:
pass
def wait_for_payment_form(self):
""" Wait for the payment form to load. """
wait = WebDriverWait(self.browser, 10)
wait.until(EC.presence_of_element_located((By.ID, 'paymentForm')))
def checkout_with_paypal(self):
""" Completes the checkout process via PayPal. """
self.wait_for_payment_form()
# Wait for the button handler to be setup
time.sleep(0.5)
# Click the payment button
self.browser.find_element_by_css_selector('#paypal').click()
self.browser.find_element_by_css_selector('button.payment-button[data-processor-name=paypal]').click()
# Wait for login form to load. PayPal's test environment is slow.
wait = WebDriverWait(self.browser, 30)
iframe_present = EC.presence_of_element_located((By.CSS_SELECTOR, '#injectedUnifiedLogin > iframe'))
iframe = wait.until(iframe_present)
login_iframe = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#injectedUnifiedLogin > iframe')))
# TODO Determine how to get past the TypeError here.
self.browser.switch_to.frame(login_iframe)
# Log into PayPal
self.browser.switch_to.frame(iframe)
email = self.browser.find_element_by_css_selector('input#email')
password = self.browser.find_element_by_css_selector('input#password')
......@@ -269,28 +269,16 @@ class PaymentMixin(object):
confirmation_button.click()
def checkout_with_cybersource(self, address):
""" Completes the checkout process via CyberSource. """
# Click the payment button
self.browser.find_element_by_css_selector('#cybersource').click()
self.dismiss_alert()
# Wait for form to load
wait = WebDriverWait(self.browser, 10)
billing_details_present = EC.presence_of_element_located((By.ID, 'billing_details'))
wait.until(billing_details_present)
# Select the credit card type (Visa) first since it triggers the display of additional fields
self.browser.find_element_by_css_selector('#card_type_001').click() # Visa
def checkout_with_credit_card(self, address):
""" Submit the payment form. """
self.wait_for_payment_form()
# Select the appropriate <option> elements
select_fields = (
('#bill_to_address_country', address['country']),
('#bill_to_address_state_us_ca', address['state']),
('#card_expiry_month', '01'),
('#card_expiry_year', '2020')
('#id_country', address['country']),
('#id_state', address['state']),
('#card-expiry-month', '12'),
('#card-expiry-year', '2030')
)
for selector, value in select_fields:
if value:
......@@ -299,24 +287,21 @@ class PaymentMixin(object):
# Fill in the text fields
billing_information = {
'bill_to_forename': 'Ed',
'bill_to_surname': 'Xavier',
'bill_to_address_line1': address['line1'],
'bill_to_address_line2': address['line2'],
'bill_to_address_city': address['city'],
'bill_to_address_postal_code': address['postal_code'],
'bill_to_email': 'edx@example.com',
'card_number': '4111111111111111',
'card_cvn': '1234'
'id_first_name': 'Ed',
'id_last_name': 'Xavier',
'id_address_line1': address['line1'],
'id_address_line2': address['line2'],
'id_city': address['city'],
'id_postal_code': address['postal_code'],
'card-number': '4111111111111111',
'card-cvn': '123'
}
for field, value in billing_information.items():
self.browser.find_element_by_css_selector('#' + field).send_keys(value)
# Click the payment button
self.browser.find_element_by_css_selector('input[type=submit]').click()
self.dismiss_alert()
self.browser.find_element_by_css_selector('#payment-button').click()
def assert_receipt_page_loads(self):
""" Verifies the receipt page loaded in the browser. """
......
# Packages required to run e2e tests
bok-choy==0.5.0
ddt==1.0.1
django-nose==1.4.2
edx-rest-api-client==1.5.0
bok-choy==0.6.2
ddt==1.1.1
django-nose==1.4.4
edx-rest-api-client==1.7.1
nose-ignore-docstring==0.2
requests==2.9.1
selenium>=2.53.1
selenium>=3.0.2
from unittest import skipUnless
import ddt
from bok_choy.web_app_test import WebAppTest
from e2e.config import VERIFIED_COURSE_ID, ENABLE_CYBERSOURCE_TESTS
from e2e.constants import CODE, CYBERSOURCE_DATA1, CYBERSOURCE_DATA2
from e2e.mixins import (CouponMixin, EcommerceApiMixin, EnrollmentApiMixin,
LogistrationMixin, UnenrollmentMixin, PaymentMixin)
from e2e.config import VERIFIED_COURSE_ID
from e2e.constants import CODE, ADDRESS_US, ADDRESS_FR
from e2e.mixins import (CouponMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin, UnenrollmentMixin,
PaymentMixin)
from e2e.pages.basket import BasketPage
from e2e.pages.coupons import CouponsCreatePage, CouponsDetailsPage, CouponsListPage, RedeemVoucherPage
from e2e.pages.ecommerce import EcommerceDashboardHomePage
......@@ -69,8 +67,7 @@ class CouponCheckoutTests(CouponMixin, UnenrollmentMixin, EcommerceApiMixin, Enr
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id, 'verified')
@skipUnless(ENABLE_CYBERSOURCE_TESTS, 'CyberSource tests are not enabled.')
@ddt.data(CYBERSOURCE_DATA1, CYBERSOURCE_DATA2)
@ddt.data(ADDRESS_US, ADDRESS_FR)
def test_discount_checkout_with_cybersource(self, address):
""" Test redemption of discount code and purchase of course via Cybersource """
self.start_redeem_flow(is_discount=True)
......
from abc import ABCMeta
from unittest import skip
from unittest import skipUnless
import ddt
......@@ -6,20 +8,28 @@ from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from e2e.config import (VERIFIED_COURSE_ID, MARKETING_URL_ROOT,
PAYPAL_PASSWORD, PAYPAL_EMAIL, ENABLE_CYBERSOURCE_TESTS,
BULK_PURCHASE_SKU)
from e2e.constants import CYBERSOURCE_DATA1, CYBERSOURCE_DATA2
from e2e.config import (VERIFIED_COURSE_ID, MARKETING_URL_ROOT, PAYPAL_PASSWORD, PAYPAL_EMAIL, BULK_PURCHASE_SKU)
from e2e.constants import ADDRESS_US, ADDRESS_FR
from e2e.mixins import (LogistrationMixin, EnrollmentApiMixin, EcommerceApiMixin,
PaymentMixin, UnenrollmentMixin)
from e2e.pages.basket import BasketAddProductPage
from e2e.pages.lms import LMSCourseModePage
from e2e.pages.marketing import MarketingCourseAboutPage
from e2e.pages.basket import BasketAddProductPage
class BasePaymentTest(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin, PaymentMixin,
WebAppTest):
__metaclass__ = ABCMeta
def setUp(self):
super(BasePaymentTest, self).setUp()
self.course_id = VERIFIED_COURSE_ID
self.username, self.password, self.email = self.get_lms_user()
self.basket_add_product_page = BasketAddProductPage(self.browser)
@ddt.ddt
class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin,
PaymentMixin, WebAppTest):
class VerifiedCertificatePaymentTests(BasePaymentTest):
def setUp(self):
super(VerifiedCertificatePaymentTests, self).setUp()
self.course_id = VERIFIED_COURSE_ID
......@@ -45,10 +55,37 @@ class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, Enro
course_modes_page = LMSCourseModePage(self.browser, self.course_id)
course_modes_page.visit()
# Click the purchase button on the track selection page to take
# the browser to the payment selection page.
# Click the purchase button on the track selection page to take the browser to the payment selection page.
self.browser.find_element_by_css_selector('input[name=verified_mode]').click()
@ddt.data(ADDRESS_US, ADDRESS_FR)
def test_checkout_with_credit_card(self, address):
""" Test the client-side checkout page.
We use a U.S. address and a French address since the checkout page requires a state for the U.S. and
Canada, but not for other countries.
"""
self._start_checkout()
self.checkout_with_credit_card(address)
self.assert_receipt_page_loads()
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id, 'verified')
@skip('See ECOM-7298.')
def test_paypal(self):
""" Test checkout with PayPal. """
self._start_checkout()
self.checkout_with_paypal()
self.assert_receipt_page_loads()
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id, 'verified')
@ddt.ddt
@skipUnless(BULK_PURCHASE_SKU, 'A bulk purchase SKU must be provided to run bulk purchase tests!')
class BulkSeatPaymentTests(BasePaymentTest):
def _start_bulk_seat_checkout(self):
""" Begin the checkout process for a verified certificate. """
self.login_with_lms(self.email, self.password)
......@@ -61,43 +98,16 @@ class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, Enro
# the browser to the payment selection page.
self.browser.find_element_by_css_selector('button[id=paypal]').click()
@skipUnless(ENABLE_CYBERSOURCE_TESTS, 'CyberSource tests are not enabled.')
@ddt.data(CYBERSOURCE_DATA1, CYBERSOURCE_DATA2)
def test_cybersource(self, address):
""" Test checkout with CyberSource. """
self._start_checkout()
self.checkout_with_cybersource(address)
self.assert_receipt_page_loads()
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id, 'verified')
@skipUnless(ENABLE_CYBERSOURCE_TESTS and BULK_PURCHASE_SKU,
'CyberSource tests are not enabled, or Bulk Purchase SKU not provided, skipping Bulk Purchase tests.')
@ddt.data(CYBERSOURCE_DATA1, CYBERSOURCE_DATA2)
def test_bulk_seat_purchase_cybersource(self, address):
def test_bulk_seat_purchase_with_credit_card(self):
""" Test bulk seat purchase checkout with CyberSource. """
self._start_bulk_seat_checkout()
self.browser.find_element_by_css_selector('button[id=cybersource]').click()
self.checkout_with_cybersource(address)
self.checkout_with_credit_card(ADDRESS_US)
self.assert_receipt_page_loads()
self.assert_user_not_enrolled(self.username, self.course_id)
def test_paypal(self):
""" Test checkout with PayPal. """
if not (PAYPAL_EMAIL and PAYPAL_PASSWORD):
self.fail('No PayPal credentials supplied!')
self._start_checkout()
self.checkout_with_paypal()
self.assert_receipt_page_loads()
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id, 'verified')
@skipUnless(BULK_PURCHASE_SKU, 'Bulk Purchase SKU not provided, skipping Bulk Purchase tests.')
def test_bulk_seat_purchase_paypal(self):
def test_bulk_seat_purchase_with_paypal(self):
""" Test bulk seat purchase checkout with PayPal. """
if not (PAYPAL_EMAIL and PAYPAL_PASSWORD):
self.fail('No PayPal credentials supplied!')
......
# Packages required for testing
-r base.txt
bok-choy==0.4.7
coverage==4.2
ddt==1.0.0
django-nose==1.4.2
bok-choy==0.6.2
coverage==4.3.4
ddt==1.1.1
django-nose==1.4.4
factory-boy==2.8.1
freezegun==0.3.7
httpretty==0.8.14
......
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