Commit b2f21ef4 by Waheed Ahmed

Complete Order History tab for students on account settings page.

ECOM-2361
parent d19098ba
"""
Stub implementation of ecommerce service for acceptance tests
"""
import re
import urlparse
from .http import StubHttpRequestHandler, StubHttpService
class StubEcommerceServiceHandler(StubHttpRequestHandler): # pylint: disable=missing-docstring
def do_GET(self): # pylint: disable=invalid-name, missing-docstring
pattern_handlers = {
'/api/v2/orders/$': self.get_orders_list,
}
if self.match_pattern(pattern_handlers):
return
self.send_response(404, content='404 Not Found')
def match_pattern(self, pattern_handlers):
"""
Find the correct handler method given the path info from the HTTP request.
"""
path = urlparse.urlparse(self.path).path
for pattern in pattern_handlers:
match = re.match(pattern, path)
if match:
pattern_handlers[pattern](**match.groupdict())
return True
return None
def get_orders_list(self):
"""
Stubs the orders list endpoint.
"""
orders = {
'results': [
{
'status': 'Complete',
'number': 'Edx-123',
'total_excl_tax': '100.0',
'date_placed': '2016-04-21T23:14:23Z',
'lines': [
{
'title': 'Test Course',
'product': {
'attribute_values': [
{
'name': 'certificate_type',
'value': 'verified'
}
]
}
}
],
}
]
}
orders = self.server.config.get('orders', orders)
self.send_json_response(orders)
class StubEcommerceService(StubHttpService): # pylint: disable=missing-docstring
HANDLER_CLASS = StubEcommerceServiceHandler
...@@ -4,7 +4,9 @@ Command-line utility to start a stub service. ...@@ -4,7 +4,9 @@ Command-line utility to start a stub service.
import sys import sys
import time import time
import logging import logging
from .comments import StubCommentsService from .comments import StubCommentsService
from .ecommerce import StubEcommerceService
from .xqueue import StubXQueueService from .xqueue import StubXQueueService
from .youtube import StubYouTubeService from .youtube import StubYouTubeService
from .lti import StubLtiService from .lti import StubLtiService
...@@ -23,6 +25,7 @@ SERVICES = { ...@@ -23,6 +25,7 @@ SERVICES = {
'video': VideoSourceHttpService, 'video': VideoSourceHttpService,
'edxnotes': StubEdxNotesService, 'edxnotes': StubEdxNotesService,
'programs': StubProgramsService, 'programs': StubProgramsService,
'ecommerce': StubEcommerceService,
} }
# Log to stdout, including debug messages # Log to stdout, including debug messages
......
...@@ -63,3 +63,19 @@ class AccountSettingsPage(FieldsMixin, PageObject): ...@@ -63,3 +63,19 @@ class AccountSettingsPage(FieldsMixin, PageObject):
Switch between the different account settings tabs. Switch between the different account settings tabs.
""" """
self.q(css='#{}'.format(tab_id)).click() self.q(css='#{}'.format(tab_id)).click()
@property
def is_order_history_tab_visible(self):
""" Check if tab with the name "Order History" is visible."""
return self.q(css='.u-field-orderHistory').visible
def get_value_of_order_history_row_item(self, field_id, field_name):
""" Return the text value of the provided order field name."""
query = self.q(css='.u-field-{} .u-field-order-{}'.format(field_id, field_name))
return query.text[0] if query.present else None
def order_button_is_visible(self, field_id):
""" Check that if hovering over the order history row shows the
order detail link or not.
"""
return self.q(css='.u-field-{} .u-field-{}'.format(field_id, 'link')).visible
...@@ -444,6 +444,28 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest): ...@@ -444,6 +444,28 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, WebAppTest):
self.assertEqual(self.account_settings_page.title_for_field(field_id), title) self.assertEqual(self.account_settings_page.title_for_field(field_id), title)
self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title) self.assertEqual(self.account_settings_page.link_title_for_link_field(field_id), link_title)
def test_order_history(self):
"""
Test that we can see orders on Order History tab.
"""
# switch to "Order History" tab
self.account_settings_page.switch_account_settings_tabs('orders-tab')
# verify that we are on correct tab
self.assertTrue(self.account_settings_page.is_order_history_tab_visible)
expected_order_data = {
'title': 'Test Course',
'date': 'Date Placed:\nApr 21, 2016',
'price': 'Cost:\n$100.0',
'number': 'Order Number:\nEdx-123'
}
for field_name, value in expected_order_data.iteritems():
self.assertEqual(
self.account_settings_page.get_value_of_order_history_row_item('order-Edx-123', field_name), value
)
self.assertTrue(self.account_settings_page.order_button_is_visible('order-Edx-123'))
@attr('a11y') @attr('a11y')
class AccountSettingsA11yTest(AccountSettingsTestMixin, WebAppTest): class AccountSettingsA11yTest(AccountSettingsTestMixin, WebAppTest):
......
[
{
"pk": 2,
"model": "commerce.commerceconfiguration",
"fields": {
"enabled": 1,
"change_date": "2016-04-21 10:19:32.034856"
}
}
]
...@@ -189,6 +189,10 @@ INSTALLED_APPS += ('coursewarehistoryextended',) ...@@ -189,6 +189,10 @@ INSTALLED_APPS += ('coursewarehistoryextended',)
BADGING_BACKEND = 'lms.djangoapps.badges.backends.tests.dummy_backend.DummyBackend' BADGING_BACKEND = 'lms.djangoapps.badges.backends.tests.dummy_backend.DummyBackend'
# Configure the LMS to use our stub eCommerce implementation
ECOMMERCE_API_URL = 'http://localhost:8043/api/v2/'
ECOMMERCE_API_SIGNING_KEY = 'ecommerce-key'
##################################################################### #####################################################################
# Lastly, see if the developer has any local overrides. # Lastly, see if the developer has any local overrides.
try: try:
......
...@@ -52,7 +52,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers ...@@ -52,7 +52,7 @@ define(['backbone', 'jquery', 'underscore', 'common/js/spec_helpers/ajax_helpers
var createAccountSettingsPage = function() { var createAccountSettingsPage = function() {
var context = AccountSettingsPage( var context = AccountSettingsPage(
FIELDS_DATA, AUTH_DATA, Helpers.USER_ACCOUNTS_API_URL, Helpers.USER_PREFERENCES_API_URL, 'edX' FIELDS_DATA, [], AUTH_DATA, Helpers.USER_ACCOUNTS_API_URL, Helpers.USER_PREFERENCES_API_URL, 'edX'
); );
return context.accountSettingsView; return context.accountSettingsView;
}; };
......
...@@ -10,9 +10,18 @@ ...@@ -10,9 +10,18 @@
], function (gettext, $, _, Backbone, Logger, UserAccountModel, UserPreferencesModel, ], function (gettext, $, _, Backbone, Logger, UserAccountModel, UserPreferencesModel,
AccountSettingsFieldViews, AccountSettingsView, StringUtils) { AccountSettingsFieldViews, AccountSettingsView, StringUtils) {
return function (fieldsData, authData, userAccountsApiUrl, userPreferencesApiUrl, accountUserId, platformName) { return function (
fieldsData,
ordersHistoryData,
authData,
userAccountsApiUrl,
userPreferencesApiUrl,
accountUserId,
platformName
) {
var accountSettingsElement, userAccountModel, userPreferencesModel, aboutSectionsData, var accountSettingsElement, userAccountModel, userPreferencesModel, aboutSectionsData,
accountsSectionData, accountSettingsView, showAccountSettingsPage, showLoadingError; accountsSectionData, ordersSectionData, accountSettingsView, showAccountSettingsPage,
showLoadingError, orderNumber;
accountSettingsElement = $('.wrapper-account-settings'); accountSettingsElement = $('.wrapper-account-settings');
...@@ -170,13 +179,49 @@ ...@@ -170,13 +179,49 @@
} }
]; ];
ordersHistoryData.unshift(
{
'title': gettext('ORDER NAME'),
'order_date': gettext('ORDER PLACED'),
'price': gettext('TOTAL'),
'number': gettext('ORDER NUMBER')
}
);
ordersSectionData = [
{
title: gettext('My Orders'),
subtitle: StringUtils.interpolate(
gettext('This page contains information about orders that you have placed with {platform_name}.'), /* jshint ignore:line */
{platform_name: platformName}
),
fields: _.map(ordersHistoryData, function(order) {
orderNumber = order.number;
if (orderNumber === 'ORDER NUMBER') {
orderNumber = 'orderId';
}
return {
'view': new AccountSettingsFieldViews.OrderHistoryFieldView({
title: order.title,
totalPrice: order.price,
orderId: order.number,
orderDate: order.order_date,
receiptUrl: order.receipt_url,
valueAttribute: 'order-' + orderNumber
})
};
})
}
];
accountSettingsView = new AccountSettingsView({ accountSettingsView = new AccountSettingsView({
model: userAccountModel, model: userAccountModel,
accountUserId: accountUserId, accountUserId: accountUserId,
el: accountSettingsElement, el: accountSettingsElement,
tabSections: { tabSections: {
aboutTabSections: aboutSectionsData, aboutTabSections: aboutSectionsData,
accountsTabSections: accountsSectionData accountsTabSections: accountsSectionData,
ordersTabSections: ordersSectionData
}, },
userPreferencesModel: userPreferencesModel userPreferencesModel: userPreferencesModel
}); });
......
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
'text!templates/fields/field_link_account.underscore', 'text!templates/fields/field_link_account.underscore',
'text!templates/fields/field_dropdown_account.underscore', 'text!templates/fields/field_dropdown_account.underscore',
'text!templates/fields/field_social_link_account.underscore', 'text!templates/fields/field_social_link_account.underscore',
'edx-ui-toolkit/js/utils/string-utils' 'text!templates/fields/field_order_history.underscore',
'edx-ui-toolkit/js/utils/string-utils',
'edx-ui-toolkit/js/utils/html-utils'
], function ( ], function (
gettext, $, _, Backbone, gettext, $, _, Backbone,
FieldViews, FieldViews,
...@@ -20,7 +22,9 @@ ...@@ -20,7 +22,9 @@
field_link_account_template, field_link_account_template,
field_dropdown_account_template, field_dropdown_account_template,
field_social_link_template, field_social_link_template,
StringUtils field_order_history_template,
StringUtils,
HtmlUtils
) )
{ {
...@@ -224,6 +228,30 @@ ...@@ -224,6 +228,30 @@
return this.indicators.success + gettext('Successfully unlinked.'); return this.indicators.success + gettext('Successfully unlinked.');
} }
}), }),
OrderHistoryFieldView: FieldViews.ReadonlyFieldView.extend({
fieldType: 'orderHistory',
fieldTemplate: field_order_history_template,
initialize: function (options) {
this.options = options;
this._super(options);
this.template = HtmlUtils.template(this.fieldTemplate);
},
render: function () {
HtmlUtils.setHtml(this.$el, this.template({
title: this.options.title,
totalPrice: this.options.totalPrice,
orderId: this.options.orderId,
orderDate: this.options.orderDate,
receiptUrl: this.options.receiptUrl,
valueAttribute: this.options.valueAttribute
}));
this.delegateEvents();
return this;
}
})
}; };
return AccountSettingsFieldViews; return AccountSettingsFieldViews;
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
activeTab: 'aboutTabSections', activeTab: 'aboutTabSections',
accountSettingsTabs: [ accountSettingsTabs: [
{name: 'aboutTabSections', id: 'about-tab', label: gettext('Account Information'), class: 'active'}, {name: 'aboutTabSections', id: 'about-tab', label: gettext('Account Information'), class: 'active'},
{name: 'accountsTabSections', id: 'accounts-tab', label: gettext('Linked Accounts')} {name: 'accountsTabSections', id: 'accounts-tab', label: gettext('Linked Accounts')},
{name: 'ordersTabSections', id: 'orders-tab', label: gettext('Order History')}
], ],
events: { events: {
'click .account-nav-link': 'changeTab' 'click .account-nav-link': 'changeTab'
......
...@@ -213,6 +213,7 @@ $dark-gray1: rgb(74,74,74); ...@@ -213,6 +213,7 @@ $dark-gray1: rgb(74,74,74);
$light-gray1: rgb(242,242,242); $light-gray1: rgb(242,242,242);
$light-gray2: rgb(171,171,171); $light-gray2: rgb(171,171,171);
$light-gray3: rgb(249,249,249); $light-gray3: rgb(249,249,249);
$light-gray4: rgb(252,252,252);
$dark-gray2: rgb(151,151,151); $dark-gray2: rgb(151,151,151);
$blue1: rgb(74,144,226); $blue1: rgb(74,144,226);
$blue2: rgb(0,161,229); $blue2: rgb(0,161,229);
......
...@@ -193,6 +193,54 @@ ...@@ -193,6 +193,54 @@
} }
} }
.u-field-order {
display: flex;
align-items: center;
font-size: em(16);
color: $gray;
width: 100%;
padding-top: $baseline;
padding-bottom: $baseline;
line-height: normal;
span {
padding: $baseline;
}
.u-field-order-title {
@include float(left);
width: 26%;
font-size: em(20);
padding-left: ($baseline*2);
}
.u-field-order-value {
@include float(left);
width: 12%;
}
.u-field-order-date {
@include float(left);
width: 16%;
}
.u-field-order-link {
width: 15%;
padding: 0;
.u-field-link {
@extend %ui-clear-button;
@extend %btn-pl-default-base;
@include font-size(14);
border: 1px solid $blue;
color: $blue;
line-height: normal;
padding: 10px;
width: 110px;
}
}
}
.social-field-linked { .social-field-linked {
background: $m-gray-l4; background: $m-gray-l4;
box-shadow: 0 1px 2px 1px $shadow-l2; box-shadow: 0 1px 2px 1px $shadow-l2;
...@@ -267,6 +315,42 @@ ...@@ -267,6 +315,42 @@
} }
} }
.u-field-orderHistory {
border-bottom: none;
border: 1px solid $m-gray-l4;
margin-bottom: $baseline;
padding: 0;
&:last-child {
border-bottom: 1px solid $m-gray-l4;
}
&:hover, &:focus {
background-color: $light-gray4;
}
}
.u-field-order-orderId {
border: none;
margin-top: $baseline;
margin-bottom: 0;
padding-bottom: 0;
&:hover, &:focus {
background-color: transparent;
}
.u-field-order {
font-weight: $font-semibold;
padding-top: 0;
padding-bottom: 0;
.u-field-order-title {
font-size: em(16);
}
}
}
.u-field-social { .u-field-social {
border-bottom: none; border-bottom: none;
margin-right: 20px; margin-right: 20px;
......
<div class="field u-field-order" <% if (receiptUrl) { %> role="group" aria-labelledby="order-title-<%- orderId %>" <% } else { %> aria-hidden="true" <% } %>>
<span class="u-field-order-title" <% if (receiptUrl) { %> id="order-title-<%- orderId %>" <% } %>><%- title %></span>
<span class="u-field-order-date"><span class="sr">Date Placed: </span><%- orderDate %></span>
<span class="u-field-order-value u-field-order-price"><span class="sr">Cost: </span><% if (!isNaN(parseFloat(totalPrice))) { %>$<% } %><%- totalPrice %></span>
<span class="u-field-order-value u-field-order-number"><span class="sr">Order Number: </span><%- orderId %></span>
<span class="u-field-order-link">
<% if (receiptUrl) { %>
<a class="u-field-link" target="_blank" href="<%- receiptUrl %>"><%- gettext('Order Details') %><span class="sr"> for <%- orderId %></span></a>
<% } %>
</span>
</div>
...@@ -32,12 +32,14 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str ...@@ -32,12 +32,14 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
<%block name="js_extra"> <%block name="js_extra">
<%static:require_module module_name="js/student_account/views/account_settings_factory" class_name="AccountSettingsFactory"> <%static:require_module module_name="js/student_account/views/account_settings_factory" class_name="AccountSettingsFactory">
var fieldsData = ${ fields | n, dump_js_escaped_json }; var fieldsData = ${ fields | n, dump_js_escaped_json },
var authData = ${ auth | n, dump_js_escaped_json }; ordersHistoryData = ${ order_history | n, dump_js_escaped_json },
var platformName = '${ static.get_platform_name() | n, js_escaped_string }'; authData = ${ auth | n, dump_js_escaped_json },
platformName = '${ static.get_platform_name() | n, js_escaped_string }';
AccountSettingsFactory( AccountSettingsFactory(
fieldsData, fieldsData,
ordersHistoryData,
authData, authData,
'${ user_accounts_api_url | n, js_escaped_string }', '${ user_accounts_api_url | n, js_escaped_string }',
'${ user_preferences_api_url | n, js_escaped_string }', '${ user_preferences_api_url | n, js_escaped_string }',
......
<% _.each(sections, function(section) { %> <% _.each(sections, function(section) { %>
<div class="section"> <div class="section">
<% if (section.subtitle) { %> <% if (section.subtitle) { %>
<p id="header-subtitle-<%- activeTabName %>" class="account-settings-header-subtitle"><%- gettext(section.subtitle) %></p> <p id="header-subtitle-<%- activeTabName %>" class="account-settings-header-subtitle"><%- section.subtitle %></p>
<% } %> <% } %>
<h3 class="section-header"><%- gettext(section.title) %></h3> <h3 class="section-header"><%- gettext(section.title) %></h3>
<div class="account-settings-section-body"> <div class="account-settings-section-body">
......
...@@ -106,6 +106,11 @@ class Env(object): ...@@ -106,6 +106,11 @@ class Env(object):
'programs': { 'programs': {
'port': 8090, 'port': 8090,
'log': BOK_CHOY_LOG_DIR / "bok_choy_programs.log", 'log': BOK_CHOY_LOG_DIR / "bok_choy_programs.log",
},
'ecommerce': {
'port': 8043,
'log': BOK_CHOY_LOG_DIR / "bok_choy_ecommerce.log",
} }
} }
......
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