codeforlife-portal 5.32.2__py2.py3-none-any.whl → 8.9.9__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfl_common/common/__init__.py +1 -0
- cfl_common/common/app_settings.py +66 -0
- cfl_common/common/apps.py +6 -0
- cfl_common/common/context_processors.py +9 -0
- cfl_common/common/csp_config.py +85 -0
- cfl_common/common/helpers/__init__.py +0 -0
- cfl_common/common/helpers/data_migration_loader.py +42 -0
- cfl_common/common/helpers/emails.py +393 -0
- cfl_common/common/helpers/generators.py +52 -0
- cfl_common/common/helpers/organisation.py +10 -0
- cfl_common/common/mail.py +201 -0
- cfl_common/common/migrations/0001_initial.py +240 -0
- cfl_common/common/migrations/0002_emailverification.py +55 -0
- cfl_common/common/migrations/0003_aimmocharacter.py +31 -0
- cfl_common/common/migrations/0004_add_aimmocharacters.py +17 -0
- cfl_common/common/migrations/0005_add_worksheets.py +8 -0
- cfl_common/common/migrations/0006_update_aimmo_character_image_path.py +17 -0
- cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +8 -0
- cfl_common/common/migrations/0008_unlock_worksheet_3.py +11 -0
- cfl_common/common/migrations/0009_add_blocked_time_to_teacher_and_student.py +24 -0
- cfl_common/common/migrations/0010_remove_teacher_title.py +18 -0
- cfl_common/common/migrations/0011_student_login_id.py +18 -0
- cfl_common/common/migrations/0012_usersession.py +39 -0
- cfl_common/common/migrations/0013_class_school.py +42 -0
- cfl_common/common/migrations/0014_login_type.py +29 -0
- cfl_common/common/migrations/0015_dailyactivity.py +31 -0
- cfl_common/common/migrations/0016_joinreleasestudent.py +42 -0
- cfl_common/common/migrations/0017_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0018_update_aimmo_character_image_path.py +15 -0
- cfl_common/common/migrations/0019_aimmocharacter_alt.py +16 -0
- cfl_common/common/migrations/0020_class_is_active_and_null_access_code.py +23 -0
- cfl_common/common/migrations/0021_school_is_active.py +28 -0
- cfl_common/common/migrations/0022_school_cleanup.py +29 -0
- cfl_common/common/migrations/0023_userprofile_aimmo_badges.py +22 -0
- cfl_common/common/migrations/0024_teacher_invited_by.py +25 -0
- cfl_common/common/migrations/0025_schoolteacherinvitation.py +47 -0
- cfl_common/common/migrations/0026_teacher_remove_join_request.py +22 -0
- cfl_common/common/migrations/0027_class_created_by.py +25 -0
- cfl_common/common/migrations/0028_coding_club_downloads.py +23 -0
- cfl_common/common/migrations/0029_dynamicelement.py +22 -0
- cfl_common/common/migrations/0030_add_maintenance_banner.py +25 -0
- cfl_common/common/migrations/0031_improve_admin_panel.py +56 -0
- cfl_common/common/migrations/0032_dailyactivity_level_control_submits.py +18 -0
- cfl_common/common/migrations/0033_password_reset_tracking_fields.py +23 -0
- cfl_common/common/migrations/0034_dailyactivity_daily_school_student_lockout_reset.py +18 -0
- cfl_common/common/migrations/0035_rename_lockout_fields.py +27 -0
- cfl_common/common/migrations/0036_rename_awaiting_email_verification_userprofile_is_verified.py +17 -0
- cfl_common/common/migrations/0037_migrate_email_verification.py +21 -0
- cfl_common/common/migrations/0038_delete_emailverification.py +16 -0
- cfl_common/common/migrations/0039_copy_email_to_username.py +18 -0
- cfl_common/common/migrations/0040_school_county.py +18 -0
- cfl_common/common/migrations/0041_populate_gb_counties.py +27 -0
- cfl_common/common/migrations/0042_totalactivity.py +25 -0
- cfl_common/common/migrations/0043_add_total_activity.py +30 -0
- cfl_common/common/migrations/0044_update_activity_models.py +33 -0
- cfl_common/common/migrations/0045_otp.py +23 -0
- cfl_common/common/migrations/0046_alter_school_country.py +19 -0
- cfl_common/common/migrations/0047_delete_school_postcode.py +16 -0
- cfl_common/common/migrations/0048_unique_school_names.py +42 -0
- cfl_common/common/migrations/0049_anonymise_orphan_users.py +29 -0
- cfl_common/common/migrations/0050_anonymise_orphan_schools.py +30 -0
- cfl_common/common/migrations/0051_verify_returning_users.py +26 -0
- cfl_common/common/migrations/0052_add_cse_fields.py +68 -0
- cfl_common/common/migrations/0053_clean_class_data.py +24 -0
- cfl_common/common/migrations/0054_delete_aimmo_models.py +20 -0
- cfl_common/common/migrations/0055_alter_schoolteacherinvitation_token.py +18 -0
- cfl_common/common/migrations/0056_set_non_school_teachers_as_non_admins.py +25 -0
- cfl_common/common/migrations/0057_teacher_teacher__is_admin.py +19 -0
- cfl_common/common/migrations/0058_userprofile_google_refresh_token_and_more.py +24 -0
- cfl_common/common/migrations/__init__.py +0 -0
- cfl_common/common/models.py +557 -0
- cfl_common/common/permissions.py +84 -0
- cfl_common/common/tests/__init__.py +0 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_schools.py +30 -0
- cfl_common/common/tests/test_migration_anonymise_orphan_users.py +30 -0
- cfl_common/common/tests/test_migration_blocked_time.py +15 -0
- cfl_common/common/tests/test_migration_remove_teacher_title.py +13 -0
- cfl_common/common/tests/test_migration_unique_school_names.py +33 -0
- cfl_common/common/tests/test_migration_verify_returning_users.py +59 -0
- cfl_common/common/tests/test_models.py +87 -0
- cfl_common/common/tests/utils/__init__.py +0 -0
- cfl_common/common/tests/utils/classes.py +38 -0
- cfl_common/common/tests/utils/email.py +67 -0
- cfl_common/common/tests/utils/organisation.py +41 -0
- cfl_common/common/tests/utils/student.py +123 -0
- cfl_common/common/tests/utils/teacher.py +73 -0
- cfl_common/common/tests/utils/user.py +27 -0
- cfl_common/common/utils.py +56 -0
- cfl_common/setup.py +61 -0
- codeforlife_portal-8.9.9.dist-info/METADATA +226 -0
- {codeforlife_portal-5.32.2.dist-info → codeforlife_portal-8.9.9.dist-info}/RECORD +341 -238
- {codeforlife_portal-5.32.2.dist-info → codeforlife_portal-8.9.9.dist-info}/WHEEL +1 -1
- codeforlife_portal-8.9.9.dist-info/licenses/LICENSE.md +3 -0
- {codeforlife_portal-5.32.2.dist-info → codeforlife_portal-8.9.9.dist-info}/top_level.txt +1 -0
- deploy/middleware/maintenance.py +25 -0
- deploy/middleware/screentime_warning.py +29 -0
- deploy/middleware/security.py +5 -6
- deploy/middleware/session_timeout.py +31 -0
- deploy/middleware/tmp_basic_auth.py +41 -0
- example_project/portal_test_settings.py +239 -0
- example_project/settings.py +156 -17
- example_project/urls.py +5 -6
- portal/__init__.py +1 -1
- portal/admin.py +142 -29
- portal/app_settings.py +10 -7
- portal/forms/dotmailer.py +6 -4
- portal/forms/invite_teacher.py +19 -10
- portal/forms/organisation.py +137 -68
- portal/forms/play.py +53 -98
- portal/forms/registration.py +70 -164
- portal/forms/teach.py +147 -121
- portal/handlers.py +1 -2
- portal/helpers/decorators.py +30 -10
- portal/helpers/password.py +86 -47
- portal/helpers/ratelimit.py +32 -15
- portal/helpers/regexes.py +5 -0
- portal/helpers/request_handlers.py +10 -0
- portal/migrations/0044_auto_20150430_0959.py +6 -2
- portal/mixins/__init__.py +1 -0
- portal/mixins/cron_mixin.py +12 -0
- portal/permissions/__init__.py +1 -0
- portal/permissions/is_cron_request_from_google.py +14 -0
- portal/static/portal/img/10_years_anniversary.png +0 -0
- portal/static/portal/img/RR_logo_grass_background.png +0 -0
- portal/static/portal/img/coding_club_hero.jpg +0 -0
- portal/static/portal/img/coding_club_python_pack.png +0 -0
- portal/static/portal/img/facebook.png +0 -0
- portal/static/portal/img/gitbook.png +0 -0
- portal/static/portal/img/howe_dell_1.png +0 -0
- portal/static/portal/img/howe_dell_2.png +0 -0
- portal/static/portal/img/howe_dell_3.png +0 -0
- portal/static/portal/img/logo_cfl.png +0 -0
- portal/static/portal/img/logo_cfl_powered.svg +35 -0
- portal/static/portal/img/logo_cfl_reminder_cards.jpg +0 -0
- portal/static/portal/img/logo_ocado_group.png +0 -0
- portal/static/portal/img/logo_python_den.svg +21 -0
- portal/static/portal/img/long_europe_map.png +0 -0
- portal/static/portal/img/python_den.png +0 -0
- portal/static/portal/img/python_den_banner.svg +26 -0
- portal/static/portal/img/rapid_router_landing_hero.png +0 -0
- portal/static/portal/img/rr_advanced.png +0 -0
- portal/static/portal/img/ten_year_map_pin.svg +1 -0
- portal/static/portal/img/thumbnail_educate_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_rapid_router.png +0 -0
- portal/static/portal/img/thumbnail_python_den.png +0 -0
- portal/static/portal/img/twitter.png +0 -0
- portal/static/portal/js/carouselCards.js +25 -0
- portal/static/portal/js/common.js +96 -1
- portal/static/portal/js/independentLogin.js +16 -0
- portal/static/portal/js/independentRegistration.js +86 -0
- portal/static/portal/js/levelControl.js +77 -0
- portal/static/portal/js/lib/jquery.min.js +2 -0
- portal/static/portal/js/organisation_manage.js +142 -14
- portal/static/portal/js/passwordStrength.js +154 -64
- portal/static/portal/js/resetPassword.js +23 -0
- portal/static/portal/js/riveted.min.js +238 -239
- portal/static/portal/js/school.js +13 -0
- portal/static/portal/js/studentLogin.js +16 -0
- portal/static/portal/js/teacherEditStudent.js +23 -0
- portal/static/portal/js/teacherLogin.js +16 -0
- portal/static/portal/js/tenYearMap.js +14 -0
- portal/static/portal/sass/colorbox.scss +0 -1
- portal/static/portal/sass/modules/_colours.scss +1 -0
- portal/static/portal/sass/modules/_levels.scss +1 -1
- portal/static/portal/sass/modules/_mixins.scss +21 -0
- portal/static/portal/sass/partials/_banners.scss +4 -177
- portal/static/portal/sass/partials/_buttons.scss +12 -15
- portal/static/portal/sass/partials/_carousel.scss +129 -0
- portal/static/portal/sass/partials/_footer.scss +21 -22
- portal/static/portal/sass/partials/_forms.scss +60 -5
- portal/static/portal/sass/partials/_grids.scss +34 -61
- portal/static/portal/sass/partials/_header.scss +28 -20
- portal/static/portal/sass/partials/_images.scss +292 -39
- portal/static/portal/sass/partials/_popup.scss +18 -15
- portal/static/portal/sass/partials/_tables.scss +12 -20
- portal/static/portal/sass/partials/_text.scss +6 -10
- portal/static/portal/sass/styles.scss +0 -1
- portal/static/portal/video/code for life .pdf +0 -0
- portal/strings/about.py +5 -0
- portal/strings/coding_club.py +9 -0
- portal/strings/play.py +6 -5
- portal/strings/teach.py +1 -1
- portal/strings/teacher_resources.py +2 -8
- portal/strings/ten_year_map.py +13 -0
- portal/templates/403.html +2 -2
- portal/templates/404.html +1 -1
- portal/templates/500.html +2 -2
- portal/templates/{captcha → django_recaptcha}/includes/js_v2_invisible.html +3 -3
- portal/templates/{captcha → django_recaptcha}/widget_v2_invisible.html +2 -2
- portal/templates/email.html +4 -2
- portal/templates/maintenance.html +34 -0
- portal/templates/portal/about.html +94 -62
- portal/templates/portal/base.html +176 -152
- portal/templates/portal/coding_club.html +100 -0
- portal/templates/portal/contribute.html +56 -52
- portal/templates/portal/email_invitation_sent.html +1 -1
- portal/templates/portal/email_style_template.html +374 -0
- portal/templates/portal/email_verification_failed.html +1 -1
- portal/templates/portal/email_verification_needed.html +9 -9
- portal/templates/portal/form_shapes.html +20 -8
- portal/templates/portal/getinvolved.html +6 -6
- portal/templates/portal/home.html +35 -10
- portal/templates/portal/home_learning.html +19 -19
- portal/templates/portal/locked_out.html +0 -1
- portal/templates/portal/locked_out_school_student.html +16 -0
- portal/templates/portal/login/independent_student.html +31 -15
- portal/templates/portal/login/student.html +10 -7
- portal/templates/portal/login/student_class_code.html +7 -4
- portal/templates/portal/login/teacher.html +34 -17
- portal/templates/portal/partials/banner.html +18 -4
- portal/templates/portal/partials/benefits.html +1 -1
- portal/templates/portal/partials/card_list.html +34 -24
- portal/templates/portal/partials/character_list.html +5 -5
- portal/templates/portal/partials/cookie_list.html +161 -0
- portal/templates/portal/partials/delete_popup.html +18 -0
- portal/templates/portal/partials/footer.html +57 -26
- portal/templates/portal/partials/header.html +118 -117
- portal/templates/portal/partials/hero_card.html +4 -3
- portal/templates/portal/partials/info_popup.html +3 -3
- portal/templates/portal/partials/invite_admin_teacher.html +23 -0
- portal/templates/portal/partials/popup.html +7 -2
- portal/templates/portal/partials/register_newsletter_tickbox.html +2 -5
- portal/templates/portal/partials/screentime_popup.html +14 -0
- portal/templates/portal/partials/service_unavailable_popup.html +17 -0
- portal/templates/portal/partials/session_popup.html +19 -0
- portal/templates/portal/play/student_dashboard.html +42 -29
- portal/templates/portal/play/student_edit_account.html +64 -9
- portal/templates/portal/play.html +61 -41
- portal/templates/portal/privacy_notice.html +697 -0
- portal/templates/portal/register.html +122 -92
- portal/templates/portal/reset_password.html +20 -40
- portal/templates/portal/reset_password_confirm.html +9 -4
- portal/templates/portal/reset_password_email_sent.html +15 -13
- portal/templates/portal/teach/base_registering.html +1 -1
- portal/templates/portal/teach/class.html +4 -6
- portal/templates/portal/teach/dashboard.html +212 -117
- portal/templates/portal/teach/invited.html +90 -0
- portal/templates/portal/teach/onboarding_classes.html +5 -3
- portal/templates/portal/teach/onboarding_print.html +1 -1
- portal/templates/portal/teach/onboarding_school.html +26 -139
- portal/templates/portal/teach/onboarding_students.html +1 -1
- portal/templates/portal/teach/teacher_dismiss_students.html +73 -55
- portal/templates/portal/teach/teacher_edit_class.html +168 -11
- portal/templates/portal/teach/teacher_edit_student.html +12 -5
- portal/templates/portal/teach/teacher_move_all_classes.html +25 -38
- portal/templates/portal/teach/teacher_move_students_to_class.html +1 -1
- portal/templates/portal/teach.html +61 -42
- portal/templates/portal/ten_year_map.html +147 -0
- portal/templates/portal/terms.html +191 -42
- portal/templates/two_factor/core/login.html +71 -59
- portal/templates/two_factor/core/setup.html +58 -49
- portal/templates/two_factor/profile/disable.html +1 -1
- portal/templates/two_factor/profile/profile.html +35 -17
- portal/templatetags/app_tags.py +59 -84
- portal/templatetags/card_list_tags.py +0 -4
- portal/tests/base_test.py +14 -3
- portal/tests/conftest.py +0 -15
- portal/tests/migrations/test_migration_make_portaladmin_teacher.py +2 -6
- portal/tests/migrations/test_migration_preview_users.py +3 -9
- portal/tests/migrations/test_migration_remove_guardian.py +1 -3
- portal/tests/migrations/test_migration_use_common_models.py +2 -6
- portal/tests/migrations/test_migration_verify_portaladmin.py +1 -3
- portal/tests/pageObjects/portal/admin/admin_base_page.py +0 -21
- portal/tests/pageObjects/portal/base_page.py +16 -26
- portal/tests/pageObjects/portal/email_verification_needed_page.py +3 -2
- portal/tests/pageObjects/portal/game_page.py +12 -19
- portal/tests/pageObjects/portal/home_page.py +13 -15
- portal/tests/pageObjects/portal/independent_login_page.py +13 -17
- portal/tests/pageObjects/portal/password_reset_form_page.py +20 -4
- portal/tests/pageObjects/portal/password_reset_page.py +25 -0
- portal/tests/pageObjects/portal/play/account_page.py +18 -27
- portal/tests/pageObjects/portal/play/dashboard_page.py +4 -4
- portal/tests/pageObjects/portal/play/join_school_or_club_page.py +8 -10
- portal/tests/pageObjects/portal/play/play_base_page.py +5 -3
- portal/tests/pageObjects/portal/signup_page.py +28 -59
- portal/tests/pageObjects/portal/student_login_class_code.py +6 -9
- portal/tests/pageObjects/portal/student_login_page.py +6 -8
- portal/tests/pageObjects/portal/teach/add_independent_student_to_class_page.py +3 -3
- portal/tests/pageObjects/portal/teach/added_independent_student_to_class_page.py +3 -1
- portal/tests/pageObjects/portal/teach/class_page.py +36 -13
- portal/tests/pageObjects/portal/teach/dashboard_page.py +43 -84
- portal/tests/pageObjects/portal/teach/dismiss_students_page.py +7 -5
- portal/tests/pageObjects/portal/teach/edit_student_page.py +10 -8
- portal/tests/pageObjects/portal/teach/move_class_page.py +5 -10
- portal/tests/pageObjects/portal/teach/move_classes_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_disambiguate_page.py +4 -2
- portal/tests/pageObjects/portal/teach/move_students_page.py +6 -13
- portal/tests/pageObjects/portal/teach/onboarding_classes_page.py +5 -3
- portal/tests/pageObjects/portal/teach/onboarding_organisation_page.py +11 -49
- portal/tests/pageObjects/portal/teach/onboarding_student_list_page.py +7 -12
- portal/tests/pageObjects/portal/teach/onboarding_students_page.py +4 -27
- portal/tests/pageObjects/portal/teach/teach_base_page.py +6 -4
- portal/tests/pageObjects/portal/teacher_login_page.py +10 -16
- portal/tests/selenium_test_case.py +3 -43
- portal/tests/snapshots/snap_test_partials.py +11 -165
- portal/tests/test_2FA.py +15 -33
- portal/tests/test_admin.py +15 -97
- portal/tests/test_api.py +212 -73
- portal/tests/test_captcha_forms.py +2 -2
- portal/tests/test_class.py +374 -24
- portal/tests/test_emails.py +83 -20
- portal/tests/{test_newsletter_footer.py → test_global_forms.py} +5 -5
- portal/tests/test_helper_methods.py +30 -0
- portal/tests/test_independent_student.py +255 -144
- portal/tests/test_invite_teacher.py +318 -10
- portal/tests/test_middleware.py +138 -15
- portal/tests/test_organisation.py +78 -262
- portal/tests/test_partials.py +0 -88
- portal/tests/test_ratelimit.py +218 -36
- portal/tests/test_school_student.py +35 -40
- portal/tests/test_security.py +12 -31
- portal/tests/test_teacher.py +425 -325
- portal/tests/test_teacher_student.py +103 -91
- portal/tests/test_views.py +900 -76
- portal/tests/utils/classes.py +2 -2
- portal/tests/utils/messages.py +13 -28
- portal/urls.py +237 -164
- portal/views/admin.py +0 -332
- portal/views/api.py +82 -68
- portal/views/cron/__init__.py +1 -0
- portal/views/cron/user.py +322 -0
- portal/views/dotmailer.py +9 -1
- portal/views/email.py +33 -77
- portal/views/google_analytics.py +28 -0
- portal/views/home.py +126 -97
- portal/views/legal.py +13 -0
- portal/views/login/independent_student.py +5 -5
- portal/views/login/student.py +51 -14
- portal/views/login/teacher.py +2 -6
- portal/views/organisation.py +20 -189
- portal/views/registration.py +97 -17
- portal/views/student/edit_account_details.py +99 -72
- portal/views/student/play.py +81 -62
- portal/views/teacher/dashboard.py +421 -149
- portal/views/teacher/teach.py +226 -177
- portal/views/two_factor/__init__.py +0 -0
- portal/views/two_factor/core.py +28 -0
- portal/views/two_factor/form.py +7 -0
- portal/views/two_factor/profile.py +11 -0
- codeforlife_portal-5.32.2.dist-info/LICENSE.md +0 -577
- codeforlife_portal-5.32.2.dist-info/METADATA +0 -38
- deploy/permissions.py +0 -2
- example_project/manage.py +0 -10
- portal/autoconfig.py +0 -140
- portal/csp_config.py +0 -60
- portal/forms/add_game.py +0 -33
- portal/helpers/location.py +0 -121
- portal/static/portal/img/kurono_hero.jpg +0 -0
- portal/static/portal/img/kurono_landing_hero.png +0 -0
- portal/static/portal/img/kurono_logo.svg +0 -1
- portal/static/portal/img/kurono_logo_grey_background.svg +0 -1
- portal/static/portal/img/kurono_logo_mark.svg +0 -1
- portal/static/portal/img/kurono_resources_hero.jpg +0 -0
- portal/static/portal/img/kurono_story.png +0 -0
- portal/static/portal/img/ocado-swirl.svg +0 -22
- portal/static/portal/img/thumbnail_educate_kurono.png +0 -0
- portal/static/portal/img/thumbnail_educate_resources_and_progress_tracking.png +0 -0
- portal/static/portal/img/thumbnail_kurono_resources.png +0 -0
- portal/static/portal/img/thumbnail_play_kurono.png +0 -0
- portal/static/portal/img/x_close_video.png +0 -0
- portal/static/portal/js/aimmoGame.js +0 -106
- portal/static/portal/js/deleteWorkspaces.js +0 -14
- portal/static/portal/js/fuzzySchoolLookup.js +0 -46
- portal/static/portal/js/lib/jquery-3.5.1.min.js +0 -2
- portal/static/portal/js/lib/jquery-ui-1.12.1.min.js +0 -13
- portal/static/portal/sass/partials/_videos.scss +0 -10
- portal/static/portal/video/aimmo_play_now_background_video.mp4 +0 -0
- portal/strings/student_aimmo_dashboard.py +0 -6
- portal/templates/portal/admin/aggregated_data.html +0 -35
- portal/templates/portal/admin/map.html +0 -70
- portal/templates/portal/mouseflow.html +0 -9
- portal/templates/portal/partials/aimmo_games_table.html +0 -83
- portal/templates/portal/partials/register_over_required_age_tickbox.html +0 -9
- portal/templates/portal/play/independent_student_dashboard.html +0 -64
- portal/templates/portal/play/student_aimmo_dashboard.html +0 -63
- portal/templates/portal/privacy_policy.html +0 -483
- portal/templates/portal/reset_password_email.html +0 -9
- portal/templates/portal/teach/invite.html +0 -25
- portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
- portal/templates/portal/teach/teacher_resources.html +0 -68
- portal/templatetags/character_list_tags.py +0 -16
- portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
- portal/tests/pageObjects/portal/student_password_reset_form_page.py +0 -23
- portal/tests/pageObjects/portal/teach/onboarding_revoke_request_page.py +0 -20
- portal/tests/pageObjects/portal/teacher_password_reset_form_page.py +0 -23
- portal/tests/test_aimmo_dashboards.py +0 -172
- portal/tests/test_location.py +0 -217
- portal/tests/utils/aimmo_games.py +0 -30
- portal/views/aimmo/dashboard.py +0 -119
- portal/views/privacy_policy.py +0 -9
- portal/views/teacher/teacher_resources.py +0 -42
- {portal/views/aimmo → cfl_common}/__init__.py +0 -0
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
from __future__ import absolute_import
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import datetime
|
|
4
|
+
from unittest.mock import ANY, Mock, patch
|
|
4
5
|
|
|
6
|
+
from common.mail import campaign_ids
|
|
7
|
+
from common.models import JoinReleaseStudent
|
|
5
8
|
from common.tests.utils import email as email_utils
|
|
6
9
|
from common.tests.utils.classes import create_class_directly
|
|
7
10
|
from common.tests.utils.organisation import (
|
|
8
11
|
create_organisation_directly,
|
|
12
|
+
join_teacher_to_organisation,
|
|
9
13
|
)
|
|
10
14
|
from common.tests.utils.student import (
|
|
11
15
|
create_independent_student,
|
|
12
16
|
create_independent_student_directly,
|
|
13
17
|
create_school_student_directly,
|
|
14
|
-
|
|
18
|
+
generate_independent_student_details,
|
|
15
19
|
verify_email,
|
|
16
20
|
)
|
|
17
21
|
from common.tests.utils.teacher import signup_teacher_directly
|
|
22
|
+
from django.contrib.auth.models import User
|
|
18
23
|
from django.core import mail
|
|
19
24
|
from django.test import Client, TestCase
|
|
20
25
|
from django.urls import reverse
|
|
26
|
+
from selenium.webdriver.common.by import By
|
|
21
27
|
from selenium.webdriver.support.wait import WebDriverWait
|
|
22
28
|
|
|
23
29
|
from portal.forms.error_messages import INVALID_LOGIN_MESSAGE
|
|
24
30
|
from .base_test import BaseTest
|
|
25
31
|
from .pageObjects.portal.home_page import HomePage
|
|
26
32
|
from .utils.messages import (
|
|
33
|
+
is_email_updated_message_showing,
|
|
27
34
|
is_email_verified_message_showing,
|
|
28
35
|
is_indep_student_join_request_received_message_showing,
|
|
29
36
|
is_indep_student_join_request_revoked_message_showing,
|
|
30
|
-
is_student_details_updated_message_showing,
|
|
31
|
-
is_email_updated_message_showing,
|
|
32
37
|
is_password_updated_message_showing,
|
|
38
|
+
is_student_details_updated_message_showing,
|
|
33
39
|
)
|
|
34
40
|
|
|
35
41
|
|
|
@@ -40,10 +46,12 @@ class TestIndependentStudent(TestCase):
|
|
|
40
46
|
response = c.post(
|
|
41
47
|
reverse("register"),
|
|
42
48
|
{
|
|
49
|
+
"independent_student_signup-date_of_birth_day": 7,
|
|
50
|
+
"independent_student_signup-date_of_birth_month": 10,
|
|
51
|
+
"independent_student_signup-date_of_birth_year": 1997,
|
|
43
52
|
"independent_student_signup-name": "Test Name",
|
|
44
|
-
"independent_student_signup-username": "TestUsername",
|
|
45
53
|
"independent_student_signup-email": "test@email.com",
|
|
46
|
-
"independent_student_signup-
|
|
54
|
+
"independent_student_signup-consent_ticked": "on",
|
|
47
55
|
"independent_student_signup-password": "pass",
|
|
48
56
|
"independent_student_signup-confirm_password": "pass",
|
|
49
57
|
"g-recaptcha-response": "something",
|
|
@@ -59,10 +67,12 @@ class TestIndependentStudent(TestCase):
|
|
|
59
67
|
response = c.post(
|
|
60
68
|
reverse("register"),
|
|
61
69
|
{
|
|
70
|
+
"independent_student_signup-date_of_birth_day": 7,
|
|
71
|
+
"independent_student_signup-date_of_birth_month": 10,
|
|
72
|
+
"independent_student_signup-date_of_birth_year": 1997,
|
|
62
73
|
"independent_student_signup-name": "Test Name",
|
|
63
|
-
"independent_student_signup-username": "TestUsername",
|
|
64
74
|
"independent_student_signup-email": "test@email.com",
|
|
65
|
-
"independent_student_signup-
|
|
75
|
+
"independent_student_signup-consent_ticked": "on",
|
|
66
76
|
"independent_student_signup-password": "Password1",
|
|
67
77
|
"independent_student_signup-confirm_password": "Password1",
|
|
68
78
|
"g-recaptcha-response": "something",
|
|
@@ -72,16 +82,34 @@ class TestIndependentStudent(TestCase):
|
|
|
72
82
|
# Assert response isn't a redirect (submit failure)
|
|
73
83
|
assert response.status_code == 200
|
|
74
84
|
|
|
85
|
+
response = c.post(
|
|
86
|
+
reverse("register"),
|
|
87
|
+
{
|
|
88
|
+
"independent_student_signup-date_of_birth_day": 7,
|
|
89
|
+
"independent_student_signup-date_of_birth_month": 10,
|
|
90
|
+
"independent_student_signup-date_of_birth_year": 1997,
|
|
91
|
+
"independent_student_signup-name": "Test Name",
|
|
92
|
+
"independent_student_signup-email": "test@email.com",
|
|
93
|
+
"independent_student_signup-consent_ticked": "on",
|
|
94
|
+
"independent_student_signup-password": "Password123$",
|
|
95
|
+
"independent_student_signup-confirm_password": "Password123$",
|
|
96
|
+
"g-recaptcha-response": "something",
|
|
97
|
+
},
|
|
98
|
+
)
|
|
99
|
+
assert response.status_code == 200
|
|
100
|
+
|
|
75
101
|
def test_signup_passwords_do_not_match_fails(self):
|
|
76
102
|
c = Client()
|
|
77
103
|
|
|
78
104
|
response = c.post(
|
|
79
105
|
reverse("register"),
|
|
80
106
|
{
|
|
107
|
+
"independent_student_signup-date_of_birth_day": 7,
|
|
108
|
+
"independent_student_signup-date_of_birth_month": 10,
|
|
109
|
+
"independent_student_signup-date_of_birth_year": 1997,
|
|
81
110
|
"independent_student_signup-name": "Test Name",
|
|
82
|
-
"independent_student_signup-username": "TestUsername",
|
|
83
111
|
"independent_student_signup-email": "test@email.com",
|
|
84
|
-
"independent_student_signup-
|
|
112
|
+
"independent_student_signup-consent_ticked": "on",
|
|
85
113
|
"independent_student_signup-password": "Password1!",
|
|
86
114
|
"independent_student_signup-confirm_password": "Password2!",
|
|
87
115
|
"g-recaptcha-response": "something",
|
|
@@ -97,12 +125,14 @@ class TestIndependentStudent(TestCase):
|
|
|
97
125
|
response = c.post(
|
|
98
126
|
reverse("register"),
|
|
99
127
|
{
|
|
128
|
+
"independent_student_signup-date_of_birth_day": 7,
|
|
129
|
+
"independent_student_signup-date_of_birth_month": 10,
|
|
130
|
+
"independent_student_signup-date_of_birth_year": 1997,
|
|
100
131
|
"independent_student_signup-name": "///",
|
|
101
|
-
"independent_student_signup-username": "TestUsername",
|
|
102
132
|
"independent_student_signup-email": "test@email.com",
|
|
103
|
-
"independent_student_signup-
|
|
104
|
-
"independent_student_signup-password": "
|
|
105
|
-
"independent_student_signup-confirm_password": "
|
|
133
|
+
"independent_student_signup-consent_ticked": "on",
|
|
134
|
+
"independent_student_signup-password": "$RRFVBGT%6yhnmju7",
|
|
135
|
+
"independent_student_signup-confirm_password": "$RRFVBGT%6yhnmju7",
|
|
106
136
|
"g-recaptcha-response": "something",
|
|
107
137
|
},
|
|
108
138
|
)
|
|
@@ -110,95 +140,132 @@ class TestIndependentStudent(TestCase):
|
|
|
110
140
|
# Assert response isn't a redirect (submit failure)
|
|
111
141
|
assert response.status_code == 200
|
|
112
142
|
|
|
113
|
-
|
|
143
|
+
@patch("common.helpers.emails.send_dotdigital_email")
|
|
144
|
+
def test_signup_under_13_sends_parent_email(self, mock_send_dotdigital_email: Mock):
|
|
114
145
|
c = Client()
|
|
115
146
|
|
|
116
147
|
response = c.post(
|
|
117
148
|
reverse("register"),
|
|
118
149
|
{
|
|
119
|
-
"independent_student_signup-
|
|
120
|
-
"independent_student_signup-
|
|
150
|
+
"independent_student_signup-date_of_birth_day": datetime.date.today().day,
|
|
151
|
+
"independent_student_signup-date_of_birth_month": datetime.date.today().month,
|
|
152
|
+
"independent_student_signup-date_of_birth_year": datetime.date.today().year,
|
|
153
|
+
"independent_student_signup-name": "Young person",
|
|
121
154
|
"independent_student_signup-email": "test@email.com",
|
|
122
|
-
"independent_student_signup-
|
|
123
|
-
"independent_student_signup-password": "
|
|
124
|
-
"independent_student_signup-confirm_password": "
|
|
155
|
+
"independent_student_signup-consent_ticked": "on",
|
|
156
|
+
"independent_student_signup-password": "$RRFVBGT%6yhnmju7",
|
|
157
|
+
"independent_student_signup-confirm_password": "$RRFVBGT%6yhnmju7",
|
|
125
158
|
"g-recaptcha-response": "something",
|
|
126
159
|
},
|
|
127
160
|
)
|
|
128
161
|
|
|
129
|
-
|
|
130
|
-
|
|
162
|
+
assert response.status_code == 302
|
|
163
|
+
mock_send_dotdigital_email.assert_called_once_with(
|
|
164
|
+
campaign_ids["verify_new_user_via_parent"], ANY, personalization_values=ANY
|
|
165
|
+
)
|
|
131
166
|
|
|
132
167
|
|
|
133
168
|
# Class for Selenium tests. We plan to replace these and turn them into Cypress tests
|
|
134
169
|
class TestIndependentStudentFrontend(BaseTest):
|
|
135
|
-
|
|
170
|
+
@patch("portal.views.registration.send_dotdigital_email")
|
|
171
|
+
def test_delete_indy_account(self, mock_send_dotdigital_email: Mock):
|
|
136
172
|
page = self.go_to_homepage()
|
|
137
|
-
page, _, _,
|
|
138
|
-
|
|
173
|
+
page, _, _, email, password = create_independent_student(page)
|
|
174
|
+
page = page.independent_student_login(email, password)
|
|
175
|
+
page = page.go_to_account_page()
|
|
176
|
+
|
|
177
|
+
# save the user to check if it was anonymised
|
|
178
|
+
user = User.objects.get(email=email)
|
|
179
|
+
user_id = user.id
|
|
139
180
|
|
|
140
|
-
|
|
181
|
+
# first check if a wrong password triggers the error
|
|
182
|
+
unsubscribe_newsletter_checkbox = page.browser.find_element(By.NAME, "unsubscribe_newsletter")
|
|
183
|
+
unsubscribe_newsletter_checkbox.click()
|
|
184
|
+
|
|
185
|
+
delete_account_form = page.browser.find_element(By.NAME, "delete_password")
|
|
186
|
+
delete_account_form.send_keys("123") # wrong password
|
|
187
|
+
|
|
188
|
+
delete_account_button = page.browser.find_element(By.ID, "delete_account_button")
|
|
189
|
+
delete_account_button.click()
|
|
190
|
+
assert (
|
|
191
|
+
page.browser.find_element(By.CSS_SELECTOR, "#form-delete-indy-account > ul > li").text
|
|
192
|
+
== "Incorrect password"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# now delete the account
|
|
196
|
+
unsubscribe_newsletter_checkbox = page.browser.find_element(By.NAME, "unsubscribe_newsletter")
|
|
197
|
+
unsubscribe_newsletter_checkbox.click()
|
|
198
|
+
|
|
199
|
+
delete_account_form = page.browser.find_element(By.NAME, "delete_password")
|
|
200
|
+
delete_account_form.send_keys(password)
|
|
201
|
+
|
|
202
|
+
delete_account_button = page.browser.find_element(By.ID, "delete_account_button")
|
|
203
|
+
delete_account_button.click()
|
|
204
|
+
|
|
205
|
+
delete_button_confirm = page.browser.find_element(By.ID, "delete_button")
|
|
206
|
+
delete_button_confirm.click()
|
|
207
|
+
|
|
208
|
+
# check if can still login to the account
|
|
209
|
+
page = self.go_to_homepage()
|
|
210
|
+
assert page.go_to_independent_student_login_page().independent_student_login_failure(email, password)
|
|
211
|
+
|
|
212
|
+
# now check if anonymised
|
|
213
|
+
assert not User.objects.get(id=user_id).is_active
|
|
214
|
+
|
|
215
|
+
# check if email has been sent
|
|
216
|
+
mock_send_dotdigital_email.assert_called_once_with(campaign_ids["delete_account"], ANY)
|
|
217
|
+
|
|
218
|
+
def test_signup_without_newsletter(self):
|
|
141
219
|
page = self.go_to_homepage()
|
|
142
|
-
page, _, _, _, _ = create_independent_student(page
|
|
220
|
+
page, _, _, _, _ = create_independent_student(page)
|
|
143
221
|
assert is_email_verified_message_showing(self.selenium)
|
|
144
222
|
|
|
145
|
-
|
|
223
|
+
@patch("portal.views.home.send_dotdigital_email")
|
|
224
|
+
def test_signup_duplicate_email_failure(self, mock_send_dotdigital_email):
|
|
146
225
|
page = self.go_to_homepage()
|
|
147
226
|
page, _, _, email, _ = create_independent_student(page)
|
|
148
227
|
assert is_email_verified_message_showing(self.selenium)
|
|
149
228
|
|
|
150
|
-
page = self.go_to_homepage()
|
|
151
|
-
|
|
152
|
-
|
|
229
|
+
page = self.go_to_homepage().go_to_signup_page()
|
|
230
|
+
|
|
231
|
+
name, username, email_address, password = generate_independent_student_details()
|
|
232
|
+
page = page.independent_student_signup(name, email, password=password, confirm_password=password)
|
|
233
|
+
page = page.return_to_home_page()
|
|
234
|
+
|
|
235
|
+
mock_send_dotdigital_email.assert_called_once_with(
|
|
236
|
+
campaign_ids["user_already_registered"], ANY, personalization_values=ANY
|
|
153
237
|
)
|
|
154
238
|
|
|
155
|
-
|
|
156
|
-
|
|
239
|
+
login_link = mock_send_dotdigital_email.call_args.kwargs["personalization_values"]["LOGIN_URL"]
|
|
240
|
+
|
|
241
|
+
page = email_utils.follow_duplicate_account_link_to_login(page, login_link, "independent")
|
|
157
242
|
|
|
158
243
|
assert self.is_login_page(page)
|
|
159
244
|
|
|
160
|
-
|
|
245
|
+
@patch("portal.views.home.send_dotdigital_email")
|
|
246
|
+
def test_signup_duplicate_email_with_teacher(self, mock_send_dotdigital_email: Mock):
|
|
161
247
|
teacher_email, _ = signup_teacher_directly()
|
|
162
248
|
|
|
163
249
|
page = self.go_to_homepage()
|
|
164
250
|
page = page.go_to_signup_page()
|
|
165
251
|
|
|
252
|
+
strong_password = "£EDCVFR$5tgbnhy6"
|
|
166
253
|
page = page.independent_student_signup(
|
|
167
|
-
"indy",
|
|
168
|
-
teacher_email,
|
|
169
|
-
teacher_email,
|
|
170
|
-
password="Password1!",
|
|
171
|
-
confirm_password="Password1!",
|
|
254
|
+
"indy", teacher_email, password=strong_password, confirm_password=strong_password
|
|
172
255
|
)
|
|
173
256
|
|
|
174
|
-
page
|
|
175
|
-
|
|
176
|
-
assert len(mail.outbox) == 1
|
|
177
|
-
assert mail.outbox[0].subject == "Code for Life: Duplicate account error"
|
|
257
|
+
page.return_to_home_page()
|
|
178
258
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
page = self.go_to_homepage()
|
|
183
|
-
page, _, _, _, _ = signup_duplicate_independent_student_fail(
|
|
184
|
-
page, duplicate_username=username
|
|
259
|
+
mock_send_dotdigital_email.assert_called_once_with(
|
|
260
|
+
campaign_ids["user_already_registered"], ANY, personalization_values=ANY
|
|
185
261
|
)
|
|
186
262
|
|
|
187
|
-
assert len(mail.outbox) == 1
|
|
188
|
-
assert mail.outbox[0].subject == "Code for Life: Username already taken"
|
|
189
|
-
|
|
190
|
-
assert self.is_login_page(page)
|
|
191
|
-
|
|
192
263
|
def test_login_failure(self):
|
|
193
264
|
page = self.go_to_homepage()
|
|
194
265
|
page = page.go_to_independent_student_login_page()
|
|
195
|
-
page = page.independent_student_login_failure(
|
|
196
|
-
"Non existent username", "Incorrect password"
|
|
197
|
-
)
|
|
266
|
+
page = page.independent_student_login_failure("non-existent-email@codeforlife.com", "Incorrect password")
|
|
198
267
|
|
|
199
|
-
assert page.has_login_failed(
|
|
200
|
-
"independent_student_login_form", INVALID_LOGIN_MESSAGE
|
|
201
|
-
)
|
|
268
|
+
assert page.has_login_failed("independent_student_login_form", INVALID_LOGIN_MESSAGE)
|
|
202
269
|
|
|
203
270
|
def test_login_success(self):
|
|
204
271
|
page = self.go_to_homepage()
|
|
@@ -206,18 +273,20 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
206
273
|
page = page.independent_student_login(username, password)
|
|
207
274
|
assert self.is_dashboard(page)
|
|
208
275
|
|
|
209
|
-
|
|
276
|
+
@patch("common.helpers.emails.send_dotdigital_email")
|
|
277
|
+
def test_login_not_verified(self, mock_send_dotdigital_email):
|
|
210
278
|
username, password, _ = create_independent_student_directly(preverified=False)
|
|
211
279
|
self.selenium.get(self.live_server_url)
|
|
212
280
|
page = HomePage(self.selenium)
|
|
213
281
|
page = page.go_to_independent_student_login_page()
|
|
214
282
|
page = page.independent_student_login_failure(username, password)
|
|
215
283
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
284
|
+
page.has_login_failed("independent_student_login_form", INVALID_LOGIN_MESSAGE)
|
|
285
|
+
assert page.has_login_failed("independent_student_login_form", INVALID_LOGIN_MESSAGE)
|
|
286
|
+
|
|
287
|
+
verification_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"]["VERIFICATION_LINK"]
|
|
219
288
|
|
|
220
|
-
verify_email(page)
|
|
289
|
+
verify_email(page, verification_url)
|
|
221
290
|
|
|
222
291
|
assert is_email_verified_message_showing(self.selenium)
|
|
223
292
|
|
|
@@ -225,17 +294,22 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
225
294
|
|
|
226
295
|
assert self.is_dashboard(page)
|
|
227
296
|
|
|
228
|
-
|
|
297
|
+
@patch("portal.forms.registration.send_dotdigital_email")
|
|
298
|
+
def test_reset_password(self, mock_send_dotdigital_email: Mock):
|
|
229
299
|
page = self.go_to_homepage()
|
|
230
300
|
|
|
231
301
|
page, name, username, _, _ = create_independent_student(page)
|
|
232
302
|
page = self.get_to_forgotten_password_page()
|
|
233
303
|
|
|
234
|
-
page.
|
|
304
|
+
page.reset_email_submit(username)
|
|
235
305
|
|
|
236
|
-
|
|
306
|
+
mock_send_dotdigital_email.assert_called_with(campaign_ids["reset_password"], ANY, personalization_values=ANY)
|
|
237
307
|
|
|
238
|
-
|
|
308
|
+
reset_password_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"][
|
|
309
|
+
"RESET_PASSWORD_LINK"
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
page = email_utils.follow_reset_email_link(self.selenium, reset_password_url)
|
|
239
313
|
|
|
240
314
|
new_password = "AnotherPassword12"
|
|
241
315
|
|
|
@@ -253,26 +327,20 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
253
327
|
page = page.go_to_account_page()
|
|
254
328
|
assert page.check_account_details({"name": name})
|
|
255
329
|
|
|
256
|
-
|
|
330
|
+
@patch("portal.forms.registration.send_dotdigital_email")
|
|
331
|
+
def test_reset_password_fail(self, mock_send_dotdigital_email: Mock):
|
|
257
332
|
page = self.get_to_forgotten_password_page()
|
|
333
|
+
fake_email = "fake_email@fakeemail.com"
|
|
334
|
+
page.reset_email_submit(fake_email)
|
|
258
335
|
|
|
259
|
-
|
|
260
|
-
page.reset_username_submit(fake_username)
|
|
261
|
-
|
|
262
|
-
time.sleep(5)
|
|
263
|
-
|
|
264
|
-
assert len(mail.outbox) == 0
|
|
336
|
+
mock_send_dotdigital_email.assert_not_called()
|
|
265
337
|
|
|
266
338
|
def test_update_name_success(self):
|
|
267
339
|
homepage = self.go_to_homepage()
|
|
268
340
|
|
|
269
|
-
play_page, name, student_username, _, password = create_independent_student(
|
|
270
|
-
homepage
|
|
271
|
-
)
|
|
341
|
+
play_page, name, student_username, _, password = create_independent_student(homepage)
|
|
272
342
|
|
|
273
|
-
page = play_page.independent_student_login(
|
|
274
|
-
student_username, password
|
|
275
|
-
).go_to_account_page()
|
|
343
|
+
page = play_page.independent_student_login(student_username, password).go_to_account_page()
|
|
276
344
|
|
|
277
345
|
assert page.check_account_details({"name": name})
|
|
278
346
|
|
|
@@ -284,9 +352,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
284
352
|
def test_update_name_failure(self):
|
|
285
353
|
homepage = self.go_to_homepage()
|
|
286
354
|
|
|
287
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
288
|
-
homepage
|
|
289
|
-
)
|
|
355
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
290
356
|
|
|
291
357
|
page = (
|
|
292
358
|
play_page.independent_student_login(student_username, password)
|
|
@@ -296,19 +362,17 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
296
362
|
|
|
297
363
|
assert self.is_account_page(page)
|
|
298
364
|
assert page.was_form_invalid(
|
|
299
|
-
"student_account_form",
|
|
300
|
-
"Names may only contain letters, numbers, dashes, underscores, and spaces.",
|
|
365
|
+
"student_account_form", "Names may only contain letters, numbers, dashes, underscores, and spaces."
|
|
301
366
|
)
|
|
302
367
|
|
|
303
|
-
|
|
368
|
+
@patch("common.helpers.emails.send_dotdigital_email")
|
|
369
|
+
def test_change_email(self, mock_send_dotdigital_email):
|
|
304
370
|
homepage = self.go_to_homepage()
|
|
305
371
|
|
|
306
|
-
_, _,
|
|
372
|
+
_, _, _, student_email, password = create_independent_student(homepage)
|
|
307
373
|
play_page, _, _, other_email, _ = create_independent_student(homepage)
|
|
308
374
|
|
|
309
|
-
page = play_page.independent_student_login(
|
|
310
|
-
student_username, password
|
|
311
|
-
).go_to_account_page()
|
|
375
|
+
page = play_page.independent_student_login(student_email, password).go_to_account_page()
|
|
312
376
|
|
|
313
377
|
# Try changing email to an existing email, should fail
|
|
314
378
|
page = page.change_email(other_email, password)
|
|
@@ -316,16 +380,16 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
316
380
|
assert is_student_details_updated_message_showing(self.selenium)
|
|
317
381
|
assert is_email_updated_message_showing(self.selenium)
|
|
318
382
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
383
|
+
mock_send_dotdigital_email.assert_called_with(
|
|
384
|
+
campaign_ids["email_change_notification"], ANY, personalization_values=ANY
|
|
385
|
+
)
|
|
322
386
|
|
|
323
387
|
# Try changing email to an existing teacher's email
|
|
324
388
|
teacher_email, _ = signup_teacher_directly()
|
|
325
389
|
|
|
326
390
|
page = (
|
|
327
391
|
homepage.go_to_independent_student_login_page()
|
|
328
|
-
.independent_student_login(
|
|
392
|
+
.independent_student_login(student_email, password)
|
|
329
393
|
.go_to_account_page()
|
|
330
394
|
)
|
|
331
395
|
|
|
@@ -334,14 +398,14 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
334
398
|
assert is_student_details_updated_message_showing(self.selenium)
|
|
335
399
|
assert is_email_updated_message_showing(self.selenium)
|
|
336
400
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
401
|
+
mock_send_dotdigital_email.assert_called_with(
|
|
402
|
+
campaign_ids["email_change_notification"], ANY, personalization_values=ANY
|
|
403
|
+
)
|
|
340
404
|
|
|
341
405
|
page = (
|
|
342
406
|
self.go_to_homepage()
|
|
343
407
|
.go_to_independent_student_login_page()
|
|
344
|
-
.independent_student_login(
|
|
408
|
+
.independent_student_login(student_email, password)
|
|
345
409
|
.go_to_account_page()
|
|
346
410
|
)
|
|
347
411
|
|
|
@@ -358,31 +422,29 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
358
422
|
page = (
|
|
359
423
|
self.go_to_homepage()
|
|
360
424
|
.go_to_independent_student_login_page()
|
|
361
|
-
.independent_student_login(
|
|
425
|
+
.independent_student_login(student_email, password)
|
|
362
426
|
)
|
|
363
427
|
assert self.is_dashboard(page)
|
|
364
428
|
|
|
365
429
|
page = page.logout()
|
|
366
430
|
|
|
367
|
-
|
|
368
|
-
|
|
431
|
+
mock_send_dotdigital_email.assert_called_with(
|
|
432
|
+
campaign_ids["email_change_verification"], ANY, personalization_values=ANY
|
|
369
433
|
)
|
|
370
|
-
|
|
434
|
+
verification_url = mock_send_dotdigital_email.call_args.kwargs["personalization_values"]["VERIFICATION_LINK"]
|
|
371
435
|
|
|
372
|
-
page =
|
|
436
|
+
page = email_utils.follow_change_email_link_to_independent_dashboard(page, verification_url)
|
|
437
|
+
|
|
438
|
+
page = page.independent_student_login(new_email, password)
|
|
373
439
|
|
|
374
440
|
assert self.is_dashboard(page)
|
|
375
441
|
|
|
376
442
|
def test_change_password(self):
|
|
377
443
|
homepage = self.go_to_homepage()
|
|
378
444
|
|
|
379
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
380
|
-
homepage
|
|
381
|
-
)
|
|
445
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
382
446
|
|
|
383
|
-
page = play_page.independent_student_login(
|
|
384
|
-
student_username, password
|
|
385
|
-
).go_to_account_page()
|
|
447
|
+
page = play_page.independent_student_login(student_username, password).go_to_account_page()
|
|
386
448
|
|
|
387
449
|
new_password = "AnotherPassword12"
|
|
388
450
|
page = page.update_password_success(new_password, password, is_independent=True)
|
|
@@ -398,9 +460,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
398
460
|
def test_join_class_nonexistent_class(self):
|
|
399
461
|
homepage = self.go_to_homepage()
|
|
400
462
|
|
|
401
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
402
|
-
homepage
|
|
403
|
-
)
|
|
463
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
404
464
|
|
|
405
465
|
page = (
|
|
406
466
|
play_page.independent_student_login(student_username, password)
|
|
@@ -410,7 +470,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
410
470
|
|
|
411
471
|
assert self.is_join_class_page(page)
|
|
412
472
|
assert page.has_join_request_failed(
|
|
413
|
-
"
|
|
473
|
+
"The class code you entered either does not exist or is not currently accepting join requests. Please double check that you have entered the correct class code and contact the teacher of the class to ensure their class is currently accepting join requests."
|
|
414
474
|
)
|
|
415
475
|
|
|
416
476
|
def test_join_class_not_accepting_requests(self):
|
|
@@ -421,9 +481,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
421
481
|
|
|
422
482
|
homepage = self.go_to_homepage()
|
|
423
483
|
|
|
424
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
425
|
-
homepage
|
|
426
|
-
)
|
|
484
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
427
485
|
|
|
428
486
|
page = (
|
|
429
487
|
play_page.independent_student_login(student_username, password)
|
|
@@ -433,7 +491,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
433
491
|
|
|
434
492
|
assert self.is_join_class_page(page)
|
|
435
493
|
assert page.has_join_request_failed(
|
|
436
|
-
"
|
|
494
|
+
"The class code you entered either does not exist or is not currently accepting join requests. Please double check that you have entered the correct class code and contact the teacher of the class to ensure their class is currently accepting join requests."
|
|
437
495
|
)
|
|
438
496
|
|
|
439
497
|
def test_join_class_revoked(self):
|
|
@@ -446,9 +504,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
446
504
|
|
|
447
505
|
homepage = self.go_to_homepage()
|
|
448
506
|
|
|
449
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
450
|
-
homepage
|
|
451
|
-
)
|
|
507
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
452
508
|
|
|
453
509
|
page = (
|
|
454
510
|
play_page.independent_student_login(student_username, password)
|
|
@@ -471,17 +527,12 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
471
527
|
klass.save()
|
|
472
528
|
|
|
473
529
|
homepage = self.go_to_homepage()
|
|
530
|
+
page = homepage.go_to_independent_student_login_page()
|
|
474
531
|
|
|
475
|
-
(
|
|
476
|
-
play_page,
|
|
477
|
-
student_name,
|
|
478
|
-
student_username,
|
|
479
|
-
_,
|
|
480
|
-
password,
|
|
481
|
-
) = create_independent_student(homepage)
|
|
532
|
+
username, password, student = create_independent_student_directly()
|
|
482
533
|
|
|
483
534
|
page = (
|
|
484
|
-
|
|
535
|
+
page.independent_student_login(username, password)
|
|
485
536
|
.go_to_join_a_school_or_club_page()
|
|
486
537
|
.join_a_school_or_club(access_code)
|
|
487
538
|
)
|
|
@@ -495,11 +546,16 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
495
546
|
.login(teacher_email, teacher_password)
|
|
496
547
|
.open_classes_tab()
|
|
497
548
|
.accept_independent_join_request()
|
|
498
|
-
.save(
|
|
549
|
+
.save(username)
|
|
499
550
|
.return_to_class()
|
|
500
551
|
)
|
|
501
552
|
|
|
502
|
-
assert page.student_exists(
|
|
553
|
+
assert page.student_exists(username)
|
|
554
|
+
|
|
555
|
+
# check whether a record is created correctly
|
|
556
|
+
logs = JoinReleaseStudent.objects.filter(student=student)
|
|
557
|
+
assert len(logs) == 1
|
|
558
|
+
assert logs[0].action_type == JoinReleaseStudent.JOIN
|
|
503
559
|
|
|
504
560
|
def test_join_class_denied(self):
|
|
505
561
|
teacher_email, teacher_password = signup_teacher_directly()
|
|
@@ -511,9 +567,7 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
511
567
|
|
|
512
568
|
homepage = self.go_to_homepage()
|
|
513
569
|
|
|
514
|
-
play_page, _, student_username, _, password = create_independent_student(
|
|
515
|
-
homepage
|
|
516
|
-
)
|
|
570
|
+
play_page, _, student_username, _, password = create_independent_student(homepage)
|
|
517
571
|
|
|
518
572
|
page = (
|
|
519
573
|
play_page.independent_student_login(student_username, password)
|
|
@@ -534,20 +588,77 @@ class TestIndependentStudentFrontend(BaseTest):
|
|
|
534
588
|
|
|
535
589
|
assert dashboard_page.has_no_independent_join_requests()
|
|
536
590
|
|
|
537
|
-
def
|
|
591
|
+
def test_join_class_denied_and_accepted_by_admin(self):
|
|
592
|
+
# Create 2 teachers in the same school, one admin, one standard
|
|
593
|
+
admin_email, admin_password1 = signup_teacher_directly()
|
|
594
|
+
standard_email, _ = signup_teacher_directly()
|
|
595
|
+
school = create_organisation_directly(admin_email)
|
|
596
|
+
join_teacher_to_organisation(standard_email, school.name, is_admin=False)
|
|
597
|
+
|
|
598
|
+
# Create class for standard teacher which always accepts external requests
|
|
599
|
+
klass, class_name, access_code = create_class_directly(standard_email)
|
|
600
|
+
klass.always_accept_requests = True
|
|
601
|
+
klass.save()
|
|
602
|
+
|
|
603
|
+
# Create two independent students
|
|
604
|
+
username1, password1, student1 = create_independent_student_directly()
|
|
605
|
+
username2, password2, student2 = create_independent_student_directly()
|
|
606
|
+
|
|
607
|
+
# Login as both students and request to join the same class
|
|
608
|
+
homepage = self.go_to_homepage()
|
|
609
|
+
page = homepage.go_to_independent_student_login_page()
|
|
610
|
+
|
|
611
|
+
page = (
|
|
612
|
+
page.independent_student_login(username1, password1)
|
|
613
|
+
.go_to_join_a_school_or_club_page()
|
|
614
|
+
.join_a_school_or_club(access_code)
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
page.logout()
|
|
618
|
+
|
|
538
619
|
page = self.go_to_homepage()
|
|
539
|
-
page
|
|
540
|
-
|
|
620
|
+
page = page.go_to_independent_student_login_page()
|
|
621
|
+
|
|
622
|
+
page = (
|
|
623
|
+
page.independent_student_login(username2, password2)
|
|
624
|
+
.go_to_join_a_school_or_club_page()
|
|
625
|
+
.join_a_school_or_club(access_code)
|
|
626
|
+
)
|
|
541
627
|
|
|
542
|
-
|
|
628
|
+
page.logout()
|
|
543
629
|
|
|
544
|
-
|
|
545
|
-
self.
|
|
630
|
+
# Login as school admin, accept the first request
|
|
631
|
+
page = self.go_to_homepage()
|
|
546
632
|
page = (
|
|
547
|
-
|
|
548
|
-
.
|
|
549
|
-
.
|
|
633
|
+
page.go_to_teacher_login_page()
|
|
634
|
+
.login(admin_email, admin_password1)
|
|
635
|
+
.open_classes_tab()
|
|
636
|
+
.accept_independent_join_request()
|
|
637
|
+
.save(username1)
|
|
638
|
+
.return_to_class()
|
|
550
639
|
)
|
|
640
|
+
|
|
641
|
+
assert page.student_exists(username1)
|
|
642
|
+
|
|
643
|
+
# check whether a record is created correctly
|
|
644
|
+
logs = JoinReleaseStudent.objects.filter(student=student1)
|
|
645
|
+
assert len(logs) == 1
|
|
646
|
+
assert logs[0].action_type == JoinReleaseStudent.JOIN
|
|
647
|
+
|
|
648
|
+
# Deny the second request
|
|
649
|
+
page = page.go_to_dashboard().open_classes_tab().deny_independent_join_request()
|
|
650
|
+
|
|
651
|
+
assert page.has_no_independent_join_requests()
|
|
652
|
+
|
|
653
|
+
# check a record hasn't been created and the student no longer has a join request
|
|
654
|
+
logs = JoinReleaseStudent.objects.filter(student=student2)
|
|
655
|
+
assert len(logs) == 0
|
|
656
|
+
assert student2.pending_class_request is None
|
|
657
|
+
assert student2.is_independent()
|
|
658
|
+
|
|
659
|
+
def get_to_forgotten_password_page(self):
|
|
660
|
+
self.selenium.get(self.live_server_url)
|
|
661
|
+
page = HomePage(self.selenium).go_to_independent_student_login_page().go_to_indep_forgotten_password_page()
|
|
551
662
|
return page
|
|
552
663
|
|
|
553
664
|
def wait_for_email(self):
|