|
4 | 4 |
|
5 | 5 |
|
6 | 6 | import datetime |
| 7 | +import json |
7 | 8 | from unittest.mock import Mock |
8 | 9 |
|
9 | 10 | import ddt |
10 | 11 | import pytest |
| 12 | + |
11 | 13 | from django.contrib.admin.sites import AdminSite |
12 | 14 | from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user |
13 | 15 | from django.forms import ValidationError |
|
24 | 26 | UserAdmin |
25 | 27 | ) |
26 | 28 | from common.djangoapps.student.models import AllowedAuthUser, CourseEnrollment, LoginFailures |
27 | | -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory |
| 29 | +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory |
28 | 30 | from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory |
29 | 31 | from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin |
30 | 32 | from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order |
@@ -532,3 +534,164 @@ def test_form_update(self): |
532 | 534 | db_allowed_auth_user = AllowedAuthUser.objects.all().first() |
533 | 535 | assert AllowedAuthUser.objects.all().count() == 1 |
534 | 536 | assert db_allowed_auth_user.email == self.other_valid_email |
| 537 | + |
| 538 | + |
| 539 | +@ddt.ddt |
| 540 | +class TestUserProfileAutocompleteAdmin(TestCase): |
| 541 | + """Tests for language and country autocomplete in UserProfile inline form via Django admin.""" |
| 542 | + |
| 543 | + def setUp(self): |
| 544 | + super().setUp() |
| 545 | + self.staff_user = UserFactory(is_staff=True) |
| 546 | + self.staff_user.set_password('test') |
| 547 | + self.staff_user.save() |
| 548 | + |
| 549 | + self.non_staff_user = UserFactory(is_staff=False) |
| 550 | + self.non_staff_user.set_password('test') |
| 551 | + self.non_staff_user.save() |
| 552 | + |
| 553 | + self.client.login(username=self.staff_user.username, password='test') |
| 554 | + |
| 555 | + self.language_url = reverse('admin:language-autocomplete') |
| 556 | + self.country_url = reverse('admin:country-autocomplete') |
| 557 | + |
| 558 | + user1 = UserFactory() |
| 559 | + user1.set_password('test') |
| 560 | + user1.save() |
| 561 | + UserProfileFactory(user=user1, language='English', country='PK') |
| 562 | + |
| 563 | + user2 = UserFactory() |
| 564 | + user2.set_password('test') |
| 565 | + user2.save() |
| 566 | + UserProfileFactory(user=user2, language='French', country='GB') |
| 567 | + |
| 568 | + user3 = UserFactory() |
| 569 | + user3.set_password('test') |
| 570 | + user3.save() |
| 571 | + UserProfileFactory(user=user3, language='German', country='US') |
| 572 | + |
| 573 | + def test_language_autocomplete_returns_expected_result(self): |
| 574 | + """Verify language autocomplete returns expected filtered results.""" |
| 575 | + profile = UserProfileFactory(user=self.staff_user, language='Esperanto') |
| 576 | + |
| 577 | + response = self.client.get(self.language_url) |
| 578 | + self.assertEqual(response.status_code, 200) |
| 579 | + |
| 580 | + data = json.loads(response.content.decode('utf-8')) |
| 581 | + self.assertTrue( |
| 582 | + any('Esperanto' in item['text'] for item in data['results']), |
| 583 | + f"Esperanto not found in: {data['results']}" |
| 584 | + ) |
| 585 | + |
| 586 | + profile.language = 'French' |
| 587 | + profile.save() |
| 588 | + |
| 589 | + response = self.client.get(f'{self.language_url}?q=Fren') |
| 590 | + self.assertEqual(response.status_code, 200) |
| 591 | + |
| 592 | + data = json.loads(response.content.decode('utf-8')) |
| 593 | + self.assertTrue( |
| 594 | + any('French' in item['text'] for item in data['results']), |
| 595 | + f"French not found in: {data['results']}" |
| 596 | + ) |
| 597 | + |
| 598 | + def test_country_autocomplete_returns_expected_result(self): |
| 599 | + """Verify country autocomplete returns expected filtered results.""" |
| 600 | + profile = UserProfileFactory(user=self.staff_user, country='SE') |
| 601 | + |
| 602 | + response = self.client.get(self.country_url) |
| 603 | + self.assertEqual(response.status_code, 200) |
| 604 | + data = json.loads(response.content.decode('utf-8')) |
| 605 | + self.assertTrue( |
| 606 | + any('Sweden' in item['text'] for item in data['results']), |
| 607 | + f"Sweden not found in: {data['results']}" |
| 608 | + ) |
| 609 | + |
| 610 | + profile.country = 'JP' |
| 611 | + profile.save() |
| 612 | + |
| 613 | + response = self.client.get(f'{self.country_url}?q=Japan') |
| 614 | + self.assertEqual(response.status_code, 200) |
| 615 | + |
| 616 | + data = json.loads(response.content.decode('utf-8')) |
| 617 | + self.assertTrue( |
| 618 | + any('Japan' in item['text'] for item in data['results']), |
| 619 | + f"Japan not found in: {data['results']}" |
| 620 | + ) |
| 621 | + |
| 622 | + @ddt.data('eng', 'fren', 'GER') |
| 623 | + def test_language_autocomplete_filters_correctly(self, term): |
| 624 | + response = self.client.get(f'{self.language_url}?q={term}') |
| 625 | + self.assertEqual(response.status_code, 200) |
| 626 | + data = json.loads(response.content) |
| 627 | + self.assertTrue(any(term.lower() in item['text'].lower() for item in data['results'])) |
| 628 | + |
| 629 | + def test_language_autocomplete_returns_empty_on_no_match(self): |
| 630 | + response = self.client.get(f'{self.language_url}?q=not-a-lang') |
| 631 | + self.assertEqual(json.loads(response.content)['results'], []) |
| 632 | + |
| 633 | + @ddt.data('United', 'Kingdom', 'Pakistan') |
| 634 | + def test_country_autocomplete_filters_correctly(self, term): |
| 635 | + response = self.client.get(f'{self.country_url}?q={term}') |
| 636 | + self.assertEqual(response.status_code, 200) |
| 637 | + data = json.loads(response.content) |
| 638 | + self.assertTrue(any(term.lower() in item['text'].lower() for item in data['results'])) |
| 639 | + |
| 640 | + def test_country_autocomplete_returns_empty_on_gibberish(self): |
| 641 | + response = self.client.get(f'{self.country_url}?q=asdfghjkl') |
| 642 | + self.assertEqual(json.loads(response.content)['results'], []) |
| 643 | + |
| 644 | + def test_admin_inline_autocomplete_urls_render(self): |
| 645 | + admin = UserFactory(is_staff=True, is_superuser=True) |
| 646 | + admin.set_password('test') |
| 647 | + admin.save() |
| 648 | + |
| 649 | + user = UserFactory() |
| 650 | + user.set_password('test') |
| 651 | + user.save() |
| 652 | + self.client.login(username=admin.username, password='test') # re-login as admin |
| 653 | + |
| 654 | + response = self.client.get(reverse('admin:auth_user_change', args=[user.id])) |
| 655 | + self.assertEqual(response.status_code, 200) |
| 656 | + self.assertContains(response, self.language_url) |
| 657 | + self.assertContains(response, self.country_url) |
| 658 | + |
| 659 | + def test_language_autocomplete_blocks_non_staff(self): |
| 660 | + self.client.logout() |
| 661 | + self.client.login(username=self.non_staff_user.username, password='test') |
| 662 | + response = self.client.get(f'{self.language_url}?q=english') |
| 663 | + data = json.loads(response.content) |
| 664 | + self.assertEqual(data['results'], []) |
| 665 | + |
| 666 | + def test_country_autocomplete_blocks_non_staff(self): |
| 667 | + self.client.logout() |
| 668 | + self.client.login(username=self.non_staff_user.username, password='test') |
| 669 | + response = self.client.get(f'{self.country_url}?q=pakistan') |
| 670 | + data = json.loads(response.content) |
| 671 | + self.assertEqual(data['results'], []) |
| 672 | + |
| 673 | + def test_language_autocomplete_blocks_anonymous_user(self): |
| 674 | + """Ensure anonymous user gets blocked or redirected.""" |
| 675 | + self.client.logout() |
| 676 | + response = self.client.get(f'{self.language_url}?q=English') |
| 677 | + self.assertIn(response.status_code, [302, 403]) |
| 678 | + |
| 679 | + def test_country_autocomplete_blocks_anonymous_user(self): |
| 680 | + """Ensure anonymous user gets blocked or redirected.""" |
| 681 | + self.client.logout() |
| 682 | + response = self.client.get(f'{self.country_url}?q=Pakistan') |
| 683 | + self.assertIn(response.status_code, [302, 403]) |
| 684 | + |
| 685 | + def test_language_autocomplete_status_for_non_staff(self): |
| 686 | + self.client.logout() |
| 687 | + self.client.login(username=self.non_staff_user.username, password='test') |
| 688 | + response = self.client.get(f'{self.language_url}?q=English') |
| 689 | + self.assertEqual(response.status_code, 200) # still 200, but empty results expected |
| 690 | + self.assertEqual(json.loads(response.content)['results'], []) |
| 691 | + |
| 692 | + def test_unknown_autocomplete_path_404s(self): |
| 693 | + logged_in = self.client.login(username=self.staff_user.username, password='test') |
| 694 | + assert logged_in, "Login failed — test user not authenticated" |
| 695 | + |
| 696 | + response = self.client.get('/admin/myapp/mymodel/fake-autocomplete/') |
| 697 | + self.assertEqual(response.status_code, 404) |
0 commit comments