FC-0001: Account pages -> micro-frontend (#30336)

* feat: Account pages. Learner Profile page

* feat: Account pages. Account Settings page

* feat: Account pages. Removed unused styles

* feat: Account pages. Removed unused toggles

* feat: fixed tests and pylint errors

* feat: update redirect to account settings for student_dashboard

* feat: fix pylint errors
This commit is contained in:
Sagirov Evgeniy
2023-03-07 14:41:52 +02:00
committed by GitHub
parent 20ef29da53
commit 0f02c7b3d9
85 changed files with 84 additions and 7528 deletions

View File

@@ -43,21 +43,6 @@ class TestComprehensiveThemeLMS(TestCase):
# This string comes from header.html of test-theme
self.assertContains(resp, "This is a footer for test-theme.")
@with_comprehensive_theme("edx.org")
def test_account_settings_hide_nav(self):
"""
Test that theme header doesn't show marketing site links for Account Settings page.
"""
self._login()
account_settings_url = reverse('account_settings')
response = self.client.get(account_settings_url)
# Verify that the header navigation links are hidden for the edx.org version
self.assertNotContains(response, "How it Works")
self.assertNotContains(response, "Find courses")
self.assertNotContains(response, "Schools & Partners")
@with_comprehensive_theme("test-theme")
def test_logo_image(self):
"""

View File

@@ -1,279 +0,0 @@
""" Views related to Account Settings. """
import logging
import urllib
from datetime import datetime
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods
from django_countries import countries
from common.djangoapps import third_party_auth
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.models import UserProfile
from common.djangoapps.third_party_auth import pipeline
from common.djangoapps.util.date_utils import strftime_localized
from lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.utils import EcommerceService
from openedx.core.djangoapps.commerce.utils import get_ecommerce_api_base_url, get_ecommerce_api_client
from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
from openedx.core.djangoapps.lang_pref.api import all_languages, released_languages
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.user_api.accounts.toggles import (
should_redirect_to_account_microfrontend,
should_redirect_to_order_history_microfrontend
)
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
from openedx.core.lib.edx_api_utils import get_api_data
from openedx.core.lib.time_zone_utils import TIME_ZONE_CHOICES
from openedx.features.enterprise_support.api import enterprise_customer_for_request
from openedx.features.enterprise_support.utils import update_account_settings_context_for_enterprise
log = logging.getLogger(__name__)
@login_required
@require_http_methods(['GET'])
def account_settings(request):
"""Render the current user's account settings page.
Args:
request (HttpRequest)
Returns:
HttpResponse: 200 if the page was sent successfully
HttpResponse: 302 if not logged in (redirect to login page)
HttpResponse: 405 if using an unsupported HTTP method
Example usage:
GET /account/settings
"""
if should_redirect_to_account_microfrontend():
url = settings.ACCOUNT_MICROFRONTEND_URL
duplicate_provider = pipeline.get_duplicate_provider(messages.get_messages(request))
if duplicate_provider:
url = '{url}?{params}'.format(
url=url,
params=urllib.parse.urlencode({
'duplicate_provider': duplicate_provider,
}),
)
return redirect(url)
context = account_settings_context(request)
return render_to_response('student_account/account_settings.html', context)
def account_settings_context(request):
""" Context for the account settings page.
Args:
request: The request object.
Returns:
dict
"""
user = request.user
year_of_birth_options = [(str(year), str(year)) for year in UserProfile.VALID_YEARS]
try:
user_orders = get_user_orders(user)
except: # pylint: disable=bare-except
log.exception('Error fetching order history from Otto.')
# Return empty order list as account settings page expect a list and
# it will be broken if exception raised
user_orders = []
beta_language = {}
dark_lang_config = DarkLangConfig.current()
if dark_lang_config.enable_beta_languages:
user_preferences = get_user_preferences(user)
pref_language = user_preferences.get('pref-lang')
if pref_language in dark_lang_config.beta_languages_list:
beta_language['code'] = pref_language
beta_language['name'] = settings.LANGUAGE_DICT.get(pref_language)
context = {
'auth': {},
'duplicate_provider': None,
'nav_hidden': True,
'fields': {
'country': {
'options': list(countries),
}, 'gender': {
'options': [(choice[0], _(choice[1])) for choice in UserProfile.GENDER_CHOICES], # lint-amnesty, pylint: disable=translation-of-non-string
}, 'language': {
'options': released_languages(),
}, 'level_of_education': {
'options': [(choice[0], _(choice[1])) for choice in UserProfile.LEVEL_OF_EDUCATION_CHOICES], # lint-amnesty, pylint: disable=translation-of-non-string
}, 'password': {
'url': reverse('password_reset'),
}, 'year_of_birth': {
'options': year_of_birth_options,
}, 'preferred_language': {
'options': all_languages(),
}, 'time_zone': {
'options': TIME_ZONE_CHOICES,
}
},
'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
'password_reset_support_link': configuration_helpers.get_value(
'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK
) or settings.SUPPORT_SITE_LINK,
'user_accounts_api_url': reverse("accounts_api", kwargs={'username': user.username}),
'user_preferences_api_url': reverse('preferences_api', kwargs={'username': user.username}),
'disable_courseware_js': True,
'show_program_listing': ProgramsApiConfig.is_enabled(),
'show_dashboard_tabs': True,
'order_history': user_orders,
'disable_order_history_tab': should_redirect_to_order_history_microfrontend(),
'enable_account_deletion': configuration_helpers.get_value(
'ENABLE_ACCOUNT_DELETION', settings.FEATURES.get('ENABLE_ACCOUNT_DELETION', False)
),
'extended_profile_fields': _get_extended_profile_fields(),
'beta_language': beta_language,
'enable_coppa_compliance': settings.ENABLE_COPPA_COMPLIANCE,
}
enterprise_customer = enterprise_customer_for_request(request)
update_account_settings_context_for_enterprise(context, enterprise_customer, user)
if third_party_auth.is_enabled():
# If the account on the third party provider is already connected with another edX account,
# we display a message to the user.
context['duplicate_provider'] = pipeline.get_duplicate_provider(messages.get_messages(request))
auth_states = pipeline.get_provider_user_states(user)
context['auth']['providers'] = [{
'id': state.provider.provider_id,
'name': state.provider.name, # The name of the provider e.g. Facebook
'connected': state.has_account, # Whether the user's edX account is connected with the provider.
# If the user is not connected, they should be directed to this page to authenticate
# with the particular provider, as long as the provider supports initiating a login.
'connect_url': pipeline.get_login_url(
state.provider.provider_id,
pipeline.AUTH_ENTRY_ACCOUNT_SETTINGS,
# The url the user should be directed to after the auth process has completed.
redirect_url=reverse('account_settings'),
),
'accepts_logins': state.provider.accepts_logins,
# If the user is connected, sending a POST request to this url removes the connection
# information for this provider from their edX account.
'disconnect_url': pipeline.get_disconnect_url(state.provider.provider_id, state.association_id),
# We only want to include providers if they are either currently available to be logged
# in with, or if the user is already authenticated with them.
} for state in auth_states if state.provider.display_for_login or state.has_account]
return context
def get_user_orders(user):
"""Given a user, get the detail of all the orders from the Ecommerce service.
Args:
user (User): The user to authenticate as when requesting ecommerce.
Returns:
list of dict, representing orders returned by the Ecommerce service.
"""
user_orders = []
commerce_configuration = CommerceConfiguration.current()
user_query = {'username': user.username}
use_cache = commerce_configuration.is_cache_enabled
cache_key = commerce_configuration.CACHE_KEY + '.' + str(user.id) if use_cache else None
commerce_user_orders = get_api_data(
commerce_configuration,
'orders',
api_client=get_ecommerce_api_client(user),
base_api_url=get_ecommerce_api_base_url(),
querystring=user_query,
cache_key=cache_key
)
for order in commerce_user_orders:
if order['status'].lower() == 'complete':
date_placed = datetime.strptime(order['date_placed'], "%Y-%m-%dT%H:%M:%SZ")
order_data = {
'number': order['number'],
'price': order['total_excl_tax'],
'order_date': strftime_localized(date_placed, 'SHORT_DATE'),
'receipt_url': EcommerceService().get_receipt_page_url(order['number']),
'lines': order['lines'],
}
user_orders.append(order_data)
return user_orders
def _get_extended_profile_fields():
"""Retrieve the extended profile fields from site configuration to be shown on the
Account Settings page
Returns:
A list of dicts. Each dict corresponds to a single field. The keys per field are:
"field_name" : name of the field stored in user_profile.meta
"field_label" : The label of the field.
"field_type" : TextField or ListField
"field_options": a list of tuples for options in the dropdown in case of ListField
"""
extended_profile_fields = []
fields_already_showing = ['username', 'name', 'email', 'pref-lang', 'country', 'time_zone', 'level_of_education',
'gender', 'year_of_birth', 'language_proficiencies', 'social_links']
field_labels_map = {
"first_name": _("First Name"),
"last_name": _("Last Name"),
"city": _("City"),
"state": _("State/Province/Region"),
"company": _("Company"),
"title": _("Title"),
"job_title": _("Job Title"),
"mailing_address": _("Mailing address"),
"goals": _("Tell us why you're interested in {platform_name}").format(
platform_name=configuration_helpers.get_value("PLATFORM_NAME", settings.PLATFORM_NAME)
),
"profession": _("Profession"),
"specialty": _("Specialty")
}
extended_profile_field_names = configuration_helpers.get_value('extended_profile_fields', [])
for field_to_exclude in fields_already_showing:
if field_to_exclude in extended_profile_field_names:
extended_profile_field_names.remove(field_to_exclude)
extended_profile_field_options = configuration_helpers.get_value('EXTRA_FIELD_OPTIONS', [])
extended_profile_field_option_tuples = {}
for field in extended_profile_field_options.keys():
field_options = extended_profile_field_options[field]
extended_profile_field_option_tuples[field] = [(option.lower(), option) for option in field_options]
for field in extended_profile_field_names:
field_dict = {
"field_name": field,
"field_label": field_labels_map.get(field, field),
}
field_options = extended_profile_field_option_tuples.get(field)
if field_options:
field_dict["field_type"] = "ListField"
field_dict["field_options"] = field_options
else:
field_dict["field_type"] = "TextField"
extended_profile_fields.append(field_dict)
return extended_profile_fields

View File

@@ -1,271 +0,0 @@
""" Tests for views related to account settings. """
from unittest import mock
from django.conf import settings
from django.contrib import messages
from django.contrib.messages.middleware import MessageMiddleware
from django.http import HttpRequest
from django.test import TestCase
from django.test.utils import override_settings
from django.urls import reverse
from edx_rest_api_client import exceptions
from edx_toggles.toggles.testutils import override_waffle_flag
from lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.tests import factories
from lms.djangoapps.commerce.tests.mocks import mock_get_orders
from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
from openedx.core.djangoapps.lang_pref.tests.test_api import EN, LT_LT
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.djangoapps.user_api.accounts.settings_views import account_settings_context, get_user_orders
from openedx.core.djangoapps.user_api.accounts.toggles import REDIRECT_TO_ACCOUNT_MICROFRONTEND
from openedx.core.djangoapps.user_api.tests.factories import UserPreferenceFactory
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.features.enterprise_support.utils import get_enterprise_readonly_account_fields
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.third_party_auth.tests.testutil import ThirdPartyAuthTestMixin
@skip_unless_lms
class AccountSettingsViewTest(ThirdPartyAuthTestMixin, SiteMixin, ProgramsApiConfigMixin, TestCase):
""" Tests for the account settings view. """
USERNAME = 'student'
PASSWORD = 'password'
FIELDS = [
'country',
'gender',
'language',
'level_of_education',
'password',
'year_of_birth',
'preferred_language',
'time_zone',
]
@mock.patch("django.conf.settings.MESSAGE_STORAGE", 'django.contrib.messages.storage.cookie.CookieStorage')
def setUp(self): # pylint: disable=arguments-differ
super().setUp()
self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD)
CommerceConfiguration.objects.create(cache_ttl=10, enabled=True)
self.client.login(username=self.USERNAME, password=self.PASSWORD)
self.request = HttpRequest()
self.request.user = self.user
# For these tests, two third party auth providers are enabled by default:
self.configure_google_provider(enabled=True, visible=True)
self.configure_facebook_provider(enabled=True, visible=True)
# Python-social saves auth failure notifcations in Django messages.
# See pipeline.get_duplicate_provider() for details.
self.request.COOKIES = {}
MessageMiddleware().process_request(self.request)
messages.error(self.request, 'Facebook is already in use.', extra_tags='Auth facebook')
@mock.patch('openedx.features.enterprise_support.api.enterprise_customer_for_request')
def test_context(self, mock_enterprise_customer_for_request):
self.request.site = SiteFactory.create()
UserPreferenceFactory(user=self.user, key='pref-lang', value='lt-lt')
DarkLangConfig(
released_languages='en',
changed_by=self.user,
enabled=True,
beta_languages='lt-lt',
enable_beta_languages=True
).save()
mock_enterprise_customer_for_request.return_value = {}
with override_settings(LANGUAGES=[EN, LT_LT], LANGUAGE_CODE='en'):
context = account_settings_context(self.request)
user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
assert context['user_accounts_api_url'] == user_accounts_api_url
user_preferences_api_url = reverse('preferences_api', kwargs={'username': self.user.username})
assert context['user_preferences_api_url'] == user_preferences_api_url
for attribute in self.FIELDS:
assert attribute in context['fields']
assert context['user_accounts_api_url'] == reverse('accounts_api', kwargs={'username': self.user.username})
assert context['user_preferences_api_url'] ==\
reverse('preferences_api', kwargs={'username': self.user.username})
assert context['duplicate_provider'] == 'facebook'
assert context['auth']['providers'][0]['name'] == 'Facebook'
assert context['auth']['providers'][1]['name'] == 'Google'
assert context['sync_learner_profile_data'] is False
assert context['edx_support_url'] == settings.SUPPORT_SITE_LINK
assert context['enterprise_name'] is None
assert context['enterprise_readonly_account_fields'] ==\
{'fields': list(get_enterprise_readonly_account_fields(self.user))}
expected_beta_language = {'code': 'lt-lt', 'name': settings.LANGUAGE_DICT.get('lt-lt')}
assert context['beta_language'] == expected_beta_language
@mock.patch('openedx.core.djangoapps.user_api.accounts.settings_views.enterprise_customer_for_request')
@mock.patch('openedx.features.enterprise_support.utils.third_party_auth.provider.Registry.get')
def test_context_for_enterprise_learner(
self, mock_get_auth_provider, mock_enterprise_customer_for_request
):
dummy_enterprise_customer = {
'uuid': 'real-ent-uuid',
'name': 'Dummy Enterprise',
'identity_provider': 'saml-ubc'
}
mock_enterprise_customer_for_request.return_value = dummy_enterprise_customer
self.request.site = SiteFactory.create()
mock_get_auth_provider.return_value.sync_learner_profile_data = True
context = account_settings_context(self.request)
user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
assert context['user_accounts_api_url'] == user_accounts_api_url
user_preferences_api_url = reverse('preferences_api', kwargs={'username': self.user.username})
assert context['user_preferences_api_url'] == user_preferences_api_url
for attribute in self.FIELDS:
assert attribute in context['fields']
assert context['user_accounts_api_url'] == reverse('accounts_api', kwargs={'username': self.user.username})
assert context['user_preferences_api_url'] ==\
reverse('preferences_api', kwargs={'username': self.user.username})
assert context['duplicate_provider'] == 'facebook'
assert context['auth']['providers'][0]['name'] == 'Facebook'
assert context['auth']['providers'][1]['name'] == 'Google'
assert context['sync_learner_profile_data'] == mock_get_auth_provider.return_value.sync_learner_profile_data
assert context['edx_support_url'] == settings.SUPPORT_SITE_LINK
assert context['enterprise_name'] == dummy_enterprise_customer['name']
assert context['enterprise_readonly_account_fields'] ==\
{'fields': list(get_enterprise_readonly_account_fields(self.user))}
def test_view(self):
"""
Test that all fields are visible
"""
view_path = reverse('account_settings')
response = self.client.get(path=view_path)
for attribute in self.FIELDS:
self.assertContains(response, attribute)
def test_header_with_programs_listing_enabled(self):
"""
Verify that tabs header will be shown while program listing is enabled.
"""
self.create_programs_config()
view_path = reverse('account_settings')
response = self.client.get(path=view_path)
self.assertContains(response, 'global-header')
def test_header_with_programs_listing_disabled(self):
"""
Verify that nav header will be shown while program listing is disabled.
"""
self.create_programs_config(enabled=False)
view_path = reverse('account_settings')
response = self.client.get(path=view_path)
self.assertContains(response, 'global-header')
def test_commerce_order_detail(self):
"""
Verify that get_user_orders returns the correct order data.
"""
with mock_get_orders():
order_detail = get_user_orders(self.user)
for i, order in enumerate(mock_get_orders.default_response['results']):
expected = {
'number': order['number'],
'price': order['total_excl_tax'],
'order_date': 'Jan 01, 2016',
'receipt_url': '/checkout/receipt/?order_number=' + order['number'],
'lines': order['lines'],
}
assert order_detail[i] == expected
def test_commerce_order_detail_exception(self):
with mock_get_orders(exception=exceptions.HttpNotFoundError):
order_detail = get_user_orders(self.user)
assert not order_detail
def test_incomplete_order_detail(self):
response = {
'results': [
factories.OrderFactory(
status='Incomplete',
lines=[
factories.OrderLineFactory(
product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory()])
)
]
)
]
}
with mock_get_orders(response=response):
order_detail = get_user_orders(self.user)
assert not order_detail
def test_order_history_with_no_product(self):
response = {
'results': [
factories.OrderFactory(
lines=[
factories.OrderLineFactory(
product=None
),
factories.OrderLineFactory(
product=factories.ProductFactory(attribute_values=[factories.ProductAttributeFactory(
name='certificate_type',
value='verified'
)])
)
]
)
]
}
with mock_get_orders(response=response):
order_detail = get_user_orders(self.user)
assert len(order_detail) == 1
def test_redirect_view(self):
old_url_path = reverse('account_settings')
with override_waffle_flag(REDIRECT_TO_ACCOUNT_MICROFRONTEND, active=True):
# Test with waffle flag active and none site setting, redirects to microfrontend
response = self.client.get(path=old_url_path)
self.assertRedirects(response, settings.ACCOUNT_MICROFRONTEND_URL, fetch_redirect_response=False)
# Test with waffle flag disabled and site setting disabled, does not redirect
response = self.client.get(path=old_url_path)
for attribute in self.FIELDS:
self.assertContains(response, attribute)
# Test with site setting disabled, does not redirect
site_domain = 'othersite.example.com'
site = self.set_up_site(site_domain, {
'SITE_NAME': site_domain,
'ENABLE_ACCOUNT_MICROFRONTEND': False
})
self.client.login(username=self.USERNAME, password=self.PASSWORD)
response = self.client.get(path=old_url_path)
for attribute in self.FIELDS:
self.assertContains(response, attribute)
# Test with site setting enabled, redirects to microfrontend
site.configuration.site_values['ENABLE_ACCOUNT_MICROFRONTEND'] = True
site.configuration.save()
site.__class__.objects.clear_cache()
response = self.client.get(path=old_url_path)
self.assertRedirects(response, settings.ACCOUNT_MICROFRONTEND_URL, fetch_redirect_response=False)

View File

@@ -1,44 +0,0 @@
"""
Toggles for accounts related code.
"""
from edx_toggles.toggles import WaffleFlag
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
# .. toggle_name: order_history.redirect_to_microfrontend
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Supports staged rollout of a new micro-frontend-based implementation of the order history page.
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2019-04-11
# .. toggle_target_removal_date: 2020-12-31
# .. toggle_warning: Also set settings.ORDER_HISTORY_MICROFRONTEND_URL and site's
# ENABLE_ORDER_HISTORY_MICROFRONTEND.
# .. toggle_tickets: DEPR-17
REDIRECT_TO_ORDER_HISTORY_MICROFRONTEND = WaffleFlag('order_history.redirect_to_microfrontend', __name__)
def should_redirect_to_order_history_microfrontend():
return (
configuration_helpers.get_value('ENABLE_ORDER_HISTORY_MICROFRONTEND') and
REDIRECT_TO_ORDER_HISTORY_MICROFRONTEND.is_enabled()
)
# .. toggle_name: account.redirect_to_microfrontend
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Supports staged rollout of a new micro-frontend-based implementation of the account page.
# Its action can be overridden using site's ENABLE_ACCOUNT_MICROFRONTEND setting.
# .. toggle_use_cases: temporary, open_edx
# .. toggle_creation_date: 2019-04-30
# .. toggle_target_removal_date: 2021-12-31
# .. toggle_warning: Also set settings.ACCOUNT_MICROFRONTEND_URL.
# .. toggle_tickets: DEPR-17
REDIRECT_TO_ACCOUNT_MICROFRONTEND = WaffleFlag('account.redirect_to_microfrontend', __name__)
def should_redirect_to_account_microfrontend():
return configuration_helpers.get_value('ENABLE_ACCOUNT_MICROFRONTEND',
REDIRECT_TO_ACCOUNT_MICROFRONTEND.is_enabled())

View File

@@ -5,7 +5,6 @@ from django.urls import path, re_path, include
from rest_framework import routers
from . import views as user_api_views
from .accounts.settings_views import account_settings
from .models import UserPreference
USER_API_ROUTER = routers.DefaultRouter()
@@ -13,7 +12,6 @@ USER_API_ROUTER.register(r'users', user_api_views.UserViewSet)
USER_API_ROUTER.register(r'user_prefs', user_api_views.UserPreferenceViewSet)
urlpatterns = [
path('account/settings', account_settings, name='account_settings'),
path('user_api/v1/', include(USER_API_ROUTER.urls)),
re_path(
fr'^user_api/v1/preferences/(?P<pref_key>{UserPreference.KEY_REGEX})/users/$',

View File

@@ -6,6 +6,7 @@ Utility functions for setting "logged in" cookies used by subdomains.
import json
import logging
import time
from urllib.parse import urljoin
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
@@ -244,8 +245,8 @@ def _get_user_info_cookie_data(request, user):
# External sites will need to have fallback mechanisms to handle this case
# (most likely just hiding the links).
try:
header_urls['account_settings'] = reverse('account_settings')
header_urls['learner_profile'] = reverse('learner_profile', kwargs={'username': user.username})
header_urls['account_settings'] = settings.ACCOUNT_MICROFRONTEND_URL
header_urls['learner_profile'] = urljoin(settings.PROFILE_MICROFRONTEND_URL, f'/u/{user.username}')
except NoReverseMatch:
pass

View File

@@ -4,6 +4,7 @@
from datetime import date
import json
from unittest.mock import MagicMock, patch
from urllib.parse import urljoin
from django.conf import settings
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
@@ -57,8 +58,8 @@ class CookieTests(TestCase):
def _get_expected_header_urls(self):
expected_header_urls = {
'logout': reverse('logout'),
'account_settings': reverse('account_settings'),
'learner_profile': reverse('learner_profile', kwargs={'username': self.user.username}),
'account_settings': settings.ACCOUNT_MICROFRONTEND_URL,
'learner_profile': urljoin(settings.PROFILE_MICROFRONTEND_URL, f'/u/{self.user.username}'),
}
block_url = retrieve_last_sitewide_block_completed(self.user)
if block_url:

View File

@@ -492,7 +492,7 @@ class LoginTest(SiteMixin, CacheIsolationTestCase, OpenEdxEventsTestMixin):
# Check that the URLs are absolute
for url in user_info["header_urls"].values():
assert 'http://testserver/' in url
assert 'http://' in url
def test_logout_deletes_mktg_cookies(self):
response, _ = self._login_response(self.user_email, self.password)