codeforlife-portal 6.46.0__py2.py3-none-any.whl → 7.0.0__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.
Potentially problematic release.
This version of codeforlife-portal might be problematic. Click here for more details.
- cfl_common/common/csp_config.py +0 -2
- cfl_common/common/migrations/0005_add_worksheets.py +1 -5
- cfl_common/common/migrations/0007_add_pdf_names_to_first_two_worksheets.py +1 -5
- 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/models.py +0 -25
- {codeforlife_portal-6.46.0.dist-info → codeforlife_portal-7.0.0.dist-info}/METADATA +3 -4
- {codeforlife_portal-6.46.0.dist-info → codeforlife_portal-7.0.0.dist-info}/RECORD +43 -66
- example_project/portal_test_settings.py +0 -1
- example_project/settings.py +0 -1
- example_project/urls.py +0 -2
- portal/__init__.py +1 -1
- portal/static/portal/sass/partials/_banners.scss +0 -177
- portal/static/portal/sass/partials/_buttons.scss +0 -12
- portal/static/portal/sass/partials/_grids.scss +0 -53
- portal/static/portal/sass/partials/_text.scss +1 -10
- portal/static/portal/sass/styles.scss +0 -1
- portal/strings/play.py +1 -2
- portal/strings/teacher_resources.py +0 -10
- portal/templates/portal/about.html +91 -60
- portal/templates/portal/contribute.html +45 -49
- portal/templates/portal/partials/header.html +0 -12
- portal/templates/portal/play/independent_student_dashboard.html +12 -25
- portal/templates/portal/play/student_dashboard.html +16 -34
- portal/templates/portal/play.html +36 -49
- portal/templates/portal/register.html +1 -1
- portal/templates/portal/teach.html +37 -55
- portal/templates/portal/ten_year_map.html +9 -9
- portal/templatetags/app_tags.py +13 -28
- portal/tests/conftest.py +4 -16
- portal/tests/pageObjects/portal/base_page.py +20 -20
- portal/tests/snapshots/snap_test_partials.py +0 -452
- portal/tests/test_class.py +213 -45
- portal/tests/test_independent_student.py +0 -9
- portal/tests/test_partials.py +6 -56
- portal/tests/test_teacher.py +221 -285
- portal/tests/test_views.py +156 -73
- portal/urls.py +23 -20
- portal/views/student/play.py +36 -25
- portal/views/teacher/teach.py +0 -5
- cfl_common/common/tests/test_migration_aimmo_characters.py +0 -29
- portal/forms/add_game.py +0 -29
- 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/thumbnail_educate_kurono.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/js/aimmoGame.js +0 -106
- 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/partials/aimmo_games_table.html +0 -89
- portal/templates/portal/play/student_aimmo_dashboard.html +0 -46
- portal/templates/portal/teach/teacher_aimmo_dashboard.html +0 -95
- portal/templatetags/character_list_tags.py +0 -16
- portal/tests/pageObjects/portal/kurono_teacher_dashboard_page.py +0 -49
- portal/tests/test_aimmo_dashboards.py +0 -206
- portal/tests/utils/aimmo_games.py +0 -30
- portal/views/aimmo/__init__.py +0 -0
- portal/views/aimmo/dashboard.py +0 -105
- {codeforlife_portal-6.46.0.dist-info → codeforlife_portal-7.0.0.dist-info}/LICENSE.md +0 -0
- {codeforlife_portal-6.46.0.dist-info → codeforlife_portal-7.0.0.dist-info}/WHEEL +0 -0
- {codeforlife_portal-6.46.0.dist-info → codeforlife_portal-7.0.0.dist-info}/top_level.txt +0 -0
portal/tests/test_teacher.py
CHANGED
|
@@ -6,9 +6,7 @@ from unittest.mock import ANY, Mock, patch
|
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
8
|
import jwt
|
|
9
|
-
from aimmo.models import Game
|
|
10
9
|
from common.mail import campaign_ids
|
|
11
|
-
from common.models import Class, Student, Teacher
|
|
12
10
|
from common.tests.utils import email as email_utils
|
|
13
11
|
from common.tests.utils.classes import create_class_directly
|
|
14
12
|
from common.tests.utils.organisation import (
|
|
@@ -50,229 +48,6 @@ from .utils.messages import (
|
|
|
50
48
|
|
|
51
49
|
|
|
52
50
|
class TestTeacher(TestCase):
|
|
53
|
-
def test_new_student_can_play_games(self):
|
|
54
|
-
"""
|
|
55
|
-
Given a teacher has a kurono game,
|
|
56
|
-
When they add a new student to their class,
|
|
57
|
-
Then the new student should be able to play that class's games
|
|
58
|
-
"""
|
|
59
|
-
email, password = signup_teacher_directly()
|
|
60
|
-
create_organisation_directly(email)
|
|
61
|
-
klass, _, access_code = create_class_directly(email)
|
|
62
|
-
create_school_student_directly(access_code)
|
|
63
|
-
|
|
64
|
-
c = Client()
|
|
65
|
-
c.login(username=email, password=password)
|
|
66
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass.id})
|
|
67
|
-
c.post(reverse("view_class", kwargs={"access_code": access_code}), {"names": "Florian"})
|
|
68
|
-
|
|
69
|
-
game = Game.objects.get(id=1)
|
|
70
|
-
new_student = Student.objects.last()
|
|
71
|
-
assert game.can_user_play(new_student.new_user)
|
|
72
|
-
|
|
73
|
-
def test_accepted_independent_student_can_play_games(self):
|
|
74
|
-
"""
|
|
75
|
-
Given an independent student requests access to a class,
|
|
76
|
-
When the teacher for that class accepts the request,
|
|
77
|
-
Then the new student should have access to that class's games
|
|
78
|
-
"""
|
|
79
|
-
email, password = signup_teacher_directly()
|
|
80
|
-
create_organisation_directly(email)
|
|
81
|
-
klass, _, access_code = create_class_directly(email)
|
|
82
|
-
klass.always_accept_requests = True
|
|
83
|
-
klass.save()
|
|
84
|
-
create_school_student_directly(access_code)
|
|
85
|
-
indep_username, indep_password, indep_student = create_independent_student_directly()
|
|
86
|
-
|
|
87
|
-
c = Client()
|
|
88
|
-
|
|
89
|
-
c.login(username=indep_username, password=indep_password)
|
|
90
|
-
c.post(reverse("student_join_organisation"), {"access_code": access_code, "class_join_request": "Request"})
|
|
91
|
-
c.logout()
|
|
92
|
-
|
|
93
|
-
c.login(username=email, password=password)
|
|
94
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass.pk})
|
|
95
|
-
c.post(reverse("teacher_accept_student_request", kwargs={"pk": indep_student.pk}), {"name": "Florian"})
|
|
96
|
-
|
|
97
|
-
game: Game = Game.objects.get(id=1)
|
|
98
|
-
new_student = Student.objects.last()
|
|
99
|
-
assert game.can_user_play(new_student.new_user)
|
|
100
|
-
|
|
101
|
-
def test_moved_class_has_correct_permissions_for_students_and_teachers(self):
|
|
102
|
-
"""
|
|
103
|
-
Given two teachers each with a class and an aimmo game,
|
|
104
|
-
When teacher 1 transfers their class to teacher 2,
|
|
105
|
-
Then:
|
|
106
|
-
- Students in each class still only have access to their class games
|
|
107
|
-
- Teacher 2 has access to both games and teacher 1 has access to none
|
|
108
|
-
"""
|
|
109
|
-
|
|
110
|
-
# Create teacher 1 -> class 1 -> student 1
|
|
111
|
-
email1, password1 = signup_teacher_directly()
|
|
112
|
-
school = create_organisation_directly(email1)
|
|
113
|
-
klass1, _, access_code1 = create_class_directly(email1, "Class 1")
|
|
114
|
-
create_school_student_directly(access_code1)
|
|
115
|
-
|
|
116
|
-
# Create teacher 2 -> class 2 -> student 2
|
|
117
|
-
email2, password2 = signup_teacher_directly()
|
|
118
|
-
join_teacher_to_organisation(email2, school.name)
|
|
119
|
-
klass2, _, access_code2 = create_class_directly(email2, "Class 2")
|
|
120
|
-
create_school_student_directly(access_code2)
|
|
121
|
-
|
|
122
|
-
teacher2: Teacher = Teacher.objects.get(new_user__email=email2)
|
|
123
|
-
# make teacher1 non admin to remove extra permissions
|
|
124
|
-
teacher1: Teacher = Teacher.objects.get(new_user__email=email1)
|
|
125
|
-
teacher1.is_admin = False
|
|
126
|
-
teacher1.save()
|
|
127
|
-
|
|
128
|
-
c = Client()
|
|
129
|
-
|
|
130
|
-
# Create game 1 under class 1
|
|
131
|
-
c.login(username=email1, password=password1)
|
|
132
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass1.pk})
|
|
133
|
-
c.logout()
|
|
134
|
-
|
|
135
|
-
# Create game 2 under class 2
|
|
136
|
-
c.login(username=email2, password=password2)
|
|
137
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass2.pk})
|
|
138
|
-
c.logout()
|
|
139
|
-
|
|
140
|
-
game1: Game = Game.objects.get(owner=teacher1.new_user)
|
|
141
|
-
game2: Game = Game.objects.get(owner=teacher2.new_user)
|
|
142
|
-
|
|
143
|
-
student1: Student = Student.objects.get(class_field=klass1)
|
|
144
|
-
student2: Student = Student.objects.get(class_field=klass2)
|
|
145
|
-
|
|
146
|
-
# Check student permissions for each game
|
|
147
|
-
assert game1.can_user_play(student1.new_user)
|
|
148
|
-
assert game2.can_user_play(student2.new_user)
|
|
149
|
-
assert not game1.can_user_play(student2.new_user)
|
|
150
|
-
assert not game2.can_user_play(student1.new_user)
|
|
151
|
-
|
|
152
|
-
# Check teacher permissions for each game
|
|
153
|
-
assert game1.can_user_play(teacher1.new_user)
|
|
154
|
-
assert game2.can_user_play(teacher2.new_user)
|
|
155
|
-
assert not game1.can_user_play(teacher2.new_user)
|
|
156
|
-
assert not game2.can_user_play(teacher1.new_user)
|
|
157
|
-
|
|
158
|
-
# Transfer class 1 over to teacher 2
|
|
159
|
-
c.login(username=email1, password=password1)
|
|
160
|
-
response = c.post(
|
|
161
|
-
reverse("teacher_edit_class", kwargs={"access_code": access_code1}),
|
|
162
|
-
{"new_teacher": teacher2.pk, "class_move_submit": ""},
|
|
163
|
-
)
|
|
164
|
-
assert response.status_code == 302
|
|
165
|
-
c.logout()
|
|
166
|
-
|
|
167
|
-
# Refresh model instances
|
|
168
|
-
klass1: Class = Class.objects.get(pk=klass1.pk)
|
|
169
|
-
game1 = Game.objects.get(pk=game1.pk)
|
|
170
|
-
game2 = Game.objects.get(pk=game2.pk)
|
|
171
|
-
|
|
172
|
-
# Check teacher 2 is the teacher for class 1
|
|
173
|
-
assert klass1.teacher == teacher2
|
|
174
|
-
|
|
175
|
-
# Check that the students' permissions have not changed
|
|
176
|
-
assert game1.can_user_play(student1.new_user)
|
|
177
|
-
assert game2.can_user_play(student2.new_user)
|
|
178
|
-
assert not game1.can_user_play(student2.new_user)
|
|
179
|
-
assert not game2.can_user_play(student1.new_user)
|
|
180
|
-
|
|
181
|
-
# Check that teacher 1 cannot access class 1's game 1 anymore
|
|
182
|
-
assert not game1.can_user_play(teacher1.new_user)
|
|
183
|
-
|
|
184
|
-
# Check that teacher 2 can access game 1
|
|
185
|
-
assert game1.can_user_play(teacher2.new_user)
|
|
186
|
-
|
|
187
|
-
def test_moved_student_has_access_to_only_new_teacher_games(self):
|
|
188
|
-
"""
|
|
189
|
-
Given a student in a class,
|
|
190
|
-
When a teacher transfers them to another class with a new teacher,
|
|
191
|
-
Then the student should only have access to the new teacher's games
|
|
192
|
-
"""
|
|
193
|
-
|
|
194
|
-
email1, password1 = signup_teacher_directly()
|
|
195
|
-
school = create_organisation_directly(email1)
|
|
196
|
-
klass1, _, access_code1 = create_class_directly(email1, "Class 1")
|
|
197
|
-
create_school_student_directly(access_code1)
|
|
198
|
-
|
|
199
|
-
email2, password2 = signup_teacher_directly()
|
|
200
|
-
join_teacher_to_organisation(email2, school.name)
|
|
201
|
-
klass2, _, access_code2 = create_class_directly(email2, "Class 2")
|
|
202
|
-
create_school_student_directly(access_code2)
|
|
203
|
-
|
|
204
|
-
teacher1 = Teacher.objects.get(new_user__email=email1)
|
|
205
|
-
teacher2 = Teacher.objects.get(new_user__email=email2)
|
|
206
|
-
|
|
207
|
-
c = Client()
|
|
208
|
-
c.login(username=email2, password=password2)
|
|
209
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass2.pk})
|
|
210
|
-
c.logout()
|
|
211
|
-
|
|
212
|
-
c.login(username=email1, password=password1)
|
|
213
|
-
c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass1.pk})
|
|
214
|
-
|
|
215
|
-
game1 = Game.objects.get(owner=teacher1.new_user)
|
|
216
|
-
game2 = Game.objects.get(owner=teacher2.new_user)
|
|
217
|
-
|
|
218
|
-
student1 = Student.objects.get(class_field=klass1)
|
|
219
|
-
student2 = Student.objects.get(class_field=klass2)
|
|
220
|
-
|
|
221
|
-
assert game1.can_user_play(student1.new_user)
|
|
222
|
-
assert game2.can_user_play(student2.new_user)
|
|
223
|
-
|
|
224
|
-
c.post(
|
|
225
|
-
reverse("teacher_move_students", kwargs={"access_code": access_code1}), {"transfer_students": student1.pk}
|
|
226
|
-
)
|
|
227
|
-
c.post(
|
|
228
|
-
reverse("teacher_move_students_to_class", kwargs={"access_code": access_code1}),
|
|
229
|
-
{
|
|
230
|
-
"form-0-name": student1.user.user.first_name,
|
|
231
|
-
"form-MAX_NUM_FORMS": 1000,
|
|
232
|
-
"form-0-orig_name": student1.user.user.first_name,
|
|
233
|
-
"form-TOTAL_FORMS": 1,
|
|
234
|
-
"form-MIN_NUM_FORMS": 0,
|
|
235
|
-
"submit_disambiguation": "",
|
|
236
|
-
"form-INITIAL_FORMS": 1,
|
|
237
|
-
"new_class": klass2.pk,
|
|
238
|
-
},
|
|
239
|
-
)
|
|
240
|
-
c.logout()
|
|
241
|
-
|
|
242
|
-
game1 = Game.objects.get(owner=teacher1.new_user)
|
|
243
|
-
game2 = Game.objects.get(owner=teacher2.new_user)
|
|
244
|
-
|
|
245
|
-
assert not game1.can_user_play(student1.new_user)
|
|
246
|
-
assert game2.can_user_play(student1.new_user)
|
|
247
|
-
|
|
248
|
-
def test_teacher_cannot_create_duplicate_game(self):
|
|
249
|
-
"""
|
|
250
|
-
Given a teacher, a class and a worksheet,
|
|
251
|
-
When the teacher creates a game for that class and worksheet, and then tries to
|
|
252
|
-
create the exact same game again,
|
|
253
|
-
Then the class should only have one game, and an error message should appear.
|
|
254
|
-
"""
|
|
255
|
-
|
|
256
|
-
email, password = signup_teacher_directly()
|
|
257
|
-
create_organisation_directly(email)
|
|
258
|
-
klass, _, _ = create_class_directly(email)
|
|
259
|
-
|
|
260
|
-
c = Client()
|
|
261
|
-
c.login(username=email, password=password)
|
|
262
|
-
game1_response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass.pk})
|
|
263
|
-
|
|
264
|
-
assert game1_response.status_code == 302
|
|
265
|
-
assert Game.objects.filter(game_class=klass, is_archived=False).count() == 1
|
|
266
|
-
assert klass.active_game is not None
|
|
267
|
-
messages = list(game1_response.wsgi_request._messages)
|
|
268
|
-
assert len([m for m in messages if m.tags == "warning"]) == 0
|
|
269
|
-
|
|
270
|
-
game2_response = c.post(reverse("teacher_aimmo_dashboard"), {"game_class": klass.pk})
|
|
271
|
-
|
|
272
|
-
messages = list(game2_response.wsgi_request._messages)
|
|
273
|
-
assert len([m for m in messages if m.tags == "warning"]) == 1
|
|
274
|
-
assert messages[0].message == "An active game already exists for this class"
|
|
275
|
-
|
|
276
51
|
def test_signup_short_password_fails(self):
|
|
277
52
|
c = Client()
|
|
278
53
|
|
|
@@ -404,7 +179,9 @@ class TestTeacher(TestCase):
|
|
|
404
179
|
assert bad_verification_response.status_code == 200
|
|
405
180
|
|
|
406
181
|
# Get verification link from function call
|
|
407
|
-
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
182
|
+
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
183
|
+
"personalization_values"
|
|
184
|
+
]["VERIFICATION_LINK"]
|
|
408
185
|
|
|
409
186
|
# Verify the email properly
|
|
410
187
|
verification_response = c.get(verification_url)
|
|
@@ -424,7 +201,14 @@ class TestTeacherFrontend(BaseTest):
|
|
|
424
201
|
def test_password_too_common(self):
|
|
425
202
|
self.selenium.get(self.live_server_url)
|
|
426
203
|
page = HomePage(self.selenium).go_to_signup_page()
|
|
427
|
-
page = page.signup(
|
|
204
|
+
page = page.signup(
|
|
205
|
+
"first_name",
|
|
206
|
+
"last_name",
|
|
207
|
+
"e@ma.il",
|
|
208
|
+
"Password123$",
|
|
209
|
+
"Password123$",
|
|
210
|
+
success=False,
|
|
211
|
+
)
|
|
428
212
|
try:
|
|
429
213
|
submit_button = WebDriverWait(self.selenium, 10).until(
|
|
430
214
|
EC.element_to_be_clickable((By.NAME, "teacher_signup"))
|
|
@@ -432,7 +216,8 @@ class TestTeacherFrontend(BaseTest):
|
|
|
432
216
|
submit_button.click()
|
|
433
217
|
except:
|
|
434
218
|
assert page.was_form_invalid(
|
|
435
|
-
"form-reg-teacher",
|
|
219
|
+
"form-reg-teacher",
|
|
220
|
+
"Password is too common, consider using a different password.",
|
|
436
221
|
)
|
|
437
222
|
|
|
438
223
|
def test_signup_without_newsletter(self):
|
|
@@ -467,8 +252,12 @@ class TestTeacherFrontend(BaseTest):
|
|
|
467
252
|
self.selenium.get(self.live_server_url)
|
|
468
253
|
page = HomePage(self.selenium)
|
|
469
254
|
page = page.go_to_teacher_login_page()
|
|
470
|
-
page = page.login_failure(
|
|
471
|
-
|
|
255
|
+
page = page.login_failure(
|
|
256
|
+
"non-existent-email@codeforlife.com", "Incorrect password"
|
|
257
|
+
)
|
|
258
|
+
assert page.has_login_failed(
|
|
259
|
+
"form-login-teacher", INVALID_LOGIN_MESSAGE
|
|
260
|
+
)
|
|
472
261
|
|
|
473
262
|
def test_login_success(self):
|
|
474
263
|
email, password = signup_teacher_directly()
|
|
@@ -492,9 +281,13 @@ class TestTeacherFrontend(BaseTest):
|
|
|
492
281
|
page = page.go_to_teacher_login_page()
|
|
493
282
|
page = page.login_failure(email, password)
|
|
494
283
|
|
|
495
|
-
assert page.has_login_failed(
|
|
284
|
+
assert page.has_login_failed(
|
|
285
|
+
"form-login-teacher", INVALID_LOGIN_MESSAGE
|
|
286
|
+
)
|
|
496
287
|
|
|
497
|
-
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
288
|
+
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
289
|
+
"personalization_values"
|
|
290
|
+
]["VERIFICATION_LINK"]
|
|
498
291
|
|
|
499
292
|
verify_email(page, verification_url)
|
|
500
293
|
|
|
@@ -518,15 +311,26 @@ class TestTeacherFrontend(BaseTest):
|
|
|
518
311
|
create_school_student_directly(access_code)
|
|
519
312
|
|
|
520
313
|
self.selenium.get(self.live_server_url)
|
|
521
|
-
page =
|
|
314
|
+
page = (
|
|
315
|
+
HomePage(self.selenium)
|
|
316
|
+
.go_to_teacher_login_page()
|
|
317
|
+
.login(email, password)
|
|
318
|
+
.open_account_tab()
|
|
319
|
+
)
|
|
522
320
|
|
|
523
321
|
page = page.change_teacher_details(
|
|
524
|
-
{
|
|
322
|
+
{
|
|
323
|
+
"first_name": "Paulina",
|
|
324
|
+
"last_name": "Koch",
|
|
325
|
+
"current_password": "$RFVBGT%6yhn",
|
|
326
|
+
}
|
|
525
327
|
)
|
|
526
328
|
assert self.is_dashboard_page(page)
|
|
527
329
|
assert is_teacher_details_updated_message_showing(self.selenium)
|
|
528
330
|
|
|
529
|
-
assert page.check_account_details(
|
|
331
|
+
assert page.check_account_details(
|
|
332
|
+
{"first_name": "Paulina", "last_name": "Koch"}
|
|
333
|
+
)
|
|
530
334
|
|
|
531
335
|
def test_edit_details_non_admin(self):
|
|
532
336
|
email_1, _ = signup_teacher_directly()
|
|
@@ -539,15 +343,26 @@ class TestTeacherFrontend(BaseTest):
|
|
|
539
343
|
create_school_student_directly(access_code_2)
|
|
540
344
|
|
|
541
345
|
self.selenium.get(self.live_server_url)
|
|
542
|
-
page =
|
|
346
|
+
page = (
|
|
347
|
+
HomePage(self.selenium)
|
|
348
|
+
.go_to_teacher_login_page()
|
|
349
|
+
.login(email_2, password_2)
|
|
350
|
+
.open_account_tab()
|
|
351
|
+
)
|
|
543
352
|
|
|
544
353
|
page = page.change_teacher_details(
|
|
545
|
-
{
|
|
354
|
+
{
|
|
355
|
+
"first_name": "Florian",
|
|
356
|
+
"last_name": "Aucomte",
|
|
357
|
+
"current_password": password_2,
|
|
358
|
+
}
|
|
546
359
|
)
|
|
547
360
|
assert self.is_dashboard_page(page)
|
|
548
361
|
assert is_teacher_details_updated_message_showing(self.selenium)
|
|
549
362
|
|
|
550
|
-
assert page.check_account_details(
|
|
363
|
+
assert page.check_account_details(
|
|
364
|
+
{"first_name": "Florian", "last_name": "Aucomte"}
|
|
365
|
+
)
|
|
551
366
|
|
|
552
367
|
@patch("common.helpers.emails.send_dotdigital_email")
|
|
553
368
|
def test_change_email(self, mock_send_dotdigital_email):
|
|
@@ -559,7 +374,11 @@ class TestTeacherFrontend(BaseTest):
|
|
|
559
374
|
other_email, _ = signup_teacher_directly()
|
|
560
375
|
|
|
561
376
|
page = self.go_to_homepage()
|
|
562
|
-
page =
|
|
377
|
+
page = (
|
|
378
|
+
page.go_to_teacher_login_page()
|
|
379
|
+
.login(email, password)
|
|
380
|
+
.open_account_tab()
|
|
381
|
+
)
|
|
563
382
|
|
|
564
383
|
# Try changing email to an existing email, should fail
|
|
565
384
|
page = page.change_email("Test", "Teacher", other_email, password)
|
|
@@ -567,24 +386,36 @@ class TestTeacherFrontend(BaseTest):
|
|
|
567
386
|
assert is_email_updated_message_showing(self.selenium)
|
|
568
387
|
|
|
569
388
|
mock_send_dotdigital_email.assert_called_with(
|
|
570
|
-
campaign_ids["email_change_notification"],
|
|
389
|
+
campaign_ids["email_change_notification"],
|
|
390
|
+
ANY,
|
|
391
|
+
personalization_values=ANY,
|
|
571
392
|
)
|
|
572
393
|
|
|
573
394
|
# Try changing email to an existing indy student's email, should fail
|
|
574
395
|
indy_email, _, _ = create_independent_student_directly()
|
|
575
396
|
page = self.go_to_homepage()
|
|
576
|
-
page =
|
|
397
|
+
page = (
|
|
398
|
+
page.go_to_teacher_login_page()
|
|
399
|
+
.login(email, password)
|
|
400
|
+
.open_account_tab()
|
|
401
|
+
)
|
|
577
402
|
|
|
578
403
|
page = page.change_email("Test", "Teacher", indy_email, password)
|
|
579
404
|
assert self.is_email_verification_page(page)
|
|
580
405
|
assert is_email_updated_message_showing(self.selenium)
|
|
581
406
|
|
|
582
407
|
mock_send_dotdigital_email.assert_called_with(
|
|
583
|
-
campaign_ids["email_change_notification"],
|
|
408
|
+
campaign_ids["email_change_notification"],
|
|
409
|
+
ANY,
|
|
410
|
+
personalization_values=ANY,
|
|
584
411
|
)
|
|
585
412
|
|
|
586
413
|
page = self.go_to_homepage()
|
|
587
|
-
page =
|
|
414
|
+
page = (
|
|
415
|
+
page.go_to_teacher_login_page()
|
|
416
|
+
.login(email, password)
|
|
417
|
+
.open_account_tab()
|
|
418
|
+
)
|
|
588
419
|
|
|
589
420
|
# Try changing email to a new one, should succeed
|
|
590
421
|
new_email = "another-email@codeforlife.com"
|
|
@@ -594,21 +425,33 @@ class TestTeacherFrontend(BaseTest):
|
|
|
594
425
|
|
|
595
426
|
# Check user can still log in with old account before verifying new email
|
|
596
427
|
self.selenium.get(self.live_server_url)
|
|
597
|
-
page =
|
|
428
|
+
page = (
|
|
429
|
+
HomePage(self.selenium)
|
|
430
|
+
.go_to_teacher_login_page()
|
|
431
|
+
.login(email, password)
|
|
432
|
+
)
|
|
598
433
|
assert self.is_dashboard_page(page)
|
|
599
434
|
|
|
600
435
|
page = page.logout()
|
|
601
436
|
|
|
602
437
|
mock_send_dotdigital_email.assert_called_with(
|
|
603
|
-
campaign_ids["email_change_verification"],
|
|
438
|
+
campaign_ids["email_change_verification"],
|
|
439
|
+
ANY,
|
|
440
|
+
personalization_values=ANY,
|
|
604
441
|
)
|
|
605
|
-
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
442
|
+
verification_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
443
|
+
"personalization_values"
|
|
444
|
+
]["VERIFICATION_LINK"]
|
|
606
445
|
|
|
607
|
-
page = email_utils.follow_change_email_link_to_dashboard(
|
|
446
|
+
page = email_utils.follow_change_email_link_to_dashboard(
|
|
447
|
+
page, verification_url
|
|
448
|
+
)
|
|
608
449
|
|
|
609
450
|
page = page.login(new_email, password).open_account_tab()
|
|
610
451
|
|
|
611
|
-
assert page.check_account_details(
|
|
452
|
+
assert page.check_account_details(
|
|
453
|
+
{"first_name": "Test", "last_name": "Teacher"}
|
|
454
|
+
)
|
|
612
455
|
|
|
613
456
|
def test_change_password(self):
|
|
614
457
|
email, password = signup_teacher_directly()
|
|
@@ -617,7 +460,12 @@ class TestTeacherFrontend(BaseTest):
|
|
|
617
460
|
create_school_student_directly(access_code)
|
|
618
461
|
|
|
619
462
|
self.selenium.get(self.live_server_url)
|
|
620
|
-
page =
|
|
463
|
+
page = (
|
|
464
|
+
HomePage(self.selenium)
|
|
465
|
+
.go_to_teacher_login_page()
|
|
466
|
+
.login(email, password)
|
|
467
|
+
.open_account_tab()
|
|
468
|
+
)
|
|
621
469
|
|
|
622
470
|
new_password = "AnotherPassword12!"
|
|
623
471
|
page = page.change_password("Test", "Teacher", new_password, password)
|
|
@@ -639,20 +487,28 @@ class TestTeacherFrontend(BaseTest):
|
|
|
639
487
|
|
|
640
488
|
page.reset_email_submit(email)
|
|
641
489
|
|
|
642
|
-
mock_send_dotdigital_email.assert_called_with(
|
|
490
|
+
mock_send_dotdigital_email.assert_called_with(
|
|
491
|
+
campaign_ids["reset_password"], ANY, personalization_values=ANY
|
|
492
|
+
)
|
|
643
493
|
|
|
644
|
-
reset_password_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
645
|
-
"
|
|
646
|
-
]
|
|
494
|
+
reset_password_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
495
|
+
"personalization_values"
|
|
496
|
+
]["RESET_PASSWORD_LINK"]
|
|
647
497
|
|
|
648
|
-
page = email_utils.follow_reset_email_link(
|
|
498
|
+
page = email_utils.follow_reset_email_link(
|
|
499
|
+
self.selenium, reset_password_url
|
|
500
|
+
)
|
|
649
501
|
|
|
650
502
|
new_password = "AnotherPassword12!"
|
|
651
503
|
|
|
652
504
|
page.teacher_reset_password(new_password)
|
|
653
505
|
|
|
654
506
|
self.selenium.get(self.live_server_url)
|
|
655
|
-
page =
|
|
507
|
+
page = (
|
|
508
|
+
HomePage(self.selenium)
|
|
509
|
+
.go_to_teacher_login_page()
|
|
510
|
+
.login(email, new_password)
|
|
511
|
+
)
|
|
656
512
|
assert self.is_dashboard_page(page)
|
|
657
513
|
|
|
658
514
|
@patch("portal.forms.registration.send_dotdigital_email")
|
|
@@ -666,18 +522,25 @@ class TestTeacherFrontend(BaseTest):
|
|
|
666
522
|
|
|
667
523
|
page.reset_email_submit(email)
|
|
668
524
|
|
|
669
|
-
mock_send_dotdigital_email.assert_called_with(
|
|
525
|
+
mock_send_dotdigital_email.assert_called_with(
|
|
526
|
+
campaign_ids["reset_password"], ANY, personalization_values=ANY
|
|
527
|
+
)
|
|
670
528
|
|
|
671
|
-
reset_password_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
672
|
-
"
|
|
673
|
-
]
|
|
529
|
+
reset_password_url = mock_send_dotdigital_email.call_args.kwargs[
|
|
530
|
+
"personalization_values"
|
|
531
|
+
]["RESET_PASSWORD_LINK"]
|
|
674
532
|
|
|
675
|
-
page = email_utils.follow_reset_email_link(
|
|
533
|
+
page = email_utils.follow_reset_email_link(
|
|
534
|
+
self.selenium, reset_password_url
|
|
535
|
+
)
|
|
676
536
|
|
|
677
537
|
page.reset_password_fail(password)
|
|
678
538
|
|
|
679
539
|
message = page.browser.find_element(By.CLASS_NAME, "errorlist")
|
|
680
|
-
assert
|
|
540
|
+
assert (
|
|
541
|
+
"Please choose a password that you haven't used before"
|
|
542
|
+
in message.text
|
|
543
|
+
)
|
|
681
544
|
|
|
682
545
|
@patch("portal.forms.registration.send_dotdigital_email")
|
|
683
546
|
def test_reset_password_fail(self, mock_send_dotdigital_email: Mock):
|
|
@@ -698,7 +561,10 @@ class TestTeacherFrontend(BaseTest):
|
|
|
698
561
|
join_teacher_to_organisation(standard_email, school.name)
|
|
699
562
|
|
|
700
563
|
page = (
|
|
701
|
-
self.go_to_homepage()
|
|
564
|
+
self.go_to_homepage()
|
|
565
|
+
.go_to_teacher_login_page()
|
|
566
|
+
.login(standard_email, standard_password)
|
|
567
|
+
.open_classes_tab()
|
|
702
568
|
)
|
|
703
569
|
|
|
704
570
|
assert page.element_does_not_exist_by_id(f"class-code-{access_code}")
|
|
@@ -710,8 +576,15 @@ class TestTeacherFrontend(BaseTest):
|
|
|
710
576
|
admin_email, admin_password = signup_teacher_directly()
|
|
711
577
|
join_teacher_to_organisation(admin_email, school.name, is_admin=True)
|
|
712
578
|
|
|
713
|
-
page =
|
|
714
|
-
|
|
579
|
+
page = (
|
|
580
|
+
self.go_to_homepage()
|
|
581
|
+
.go_to_teacher_login_page()
|
|
582
|
+
.login(admin_email, admin_password)
|
|
583
|
+
.open_classes_tab()
|
|
584
|
+
)
|
|
585
|
+
class_code_field = page.browser.find_element(
|
|
586
|
+
By.ID, f"class-code-{access_code}"
|
|
587
|
+
)
|
|
715
588
|
assert class_code_field.text == access_code
|
|
716
589
|
|
|
717
590
|
def test_admin_student_edit(self):
|
|
@@ -719,13 +592,20 @@ class TestTeacherFrontend(BaseTest):
|
|
|
719
592
|
school = create_organisation_directly(email)
|
|
720
593
|
|
|
721
594
|
klass, _, access_code = create_class_directly(email, "class123")
|
|
722
|
-
|
|
595
|
+
(
|
|
596
|
+
student_name,
|
|
597
|
+
student_password,
|
|
598
|
+
student_student,
|
|
599
|
+
) = create_school_student_directly(access_code)
|
|
723
600
|
|
|
724
601
|
joining_email, joining_password = signup_teacher_directly()
|
|
725
602
|
join_teacher_to_organisation(joining_email, school.name, is_admin=True)
|
|
726
603
|
|
|
727
604
|
page = (
|
|
728
|
-
self.go_to_homepage()
|
|
605
|
+
self.go_to_homepage()
|
|
606
|
+
.go_to_teacher_login_page()
|
|
607
|
+
.login(joining_email, joining_password)
|
|
608
|
+
.open_classes_tab()
|
|
729
609
|
)
|
|
730
610
|
|
|
731
611
|
class_button = WebDriverWait(self.selenium, WAIT_TIME).until(
|
|
@@ -739,21 +619,34 @@ class TestTeacherFrontend(BaseTest):
|
|
|
739
619
|
edit_student_button.click()
|
|
740
620
|
|
|
741
621
|
title = page.browser.find_element(By.ID, "student_details")
|
|
742
|
-
assert
|
|
622
|
+
assert (
|
|
623
|
+
title.text
|
|
624
|
+
== f"Edit student details for {student_name} from class {klass} ({access_code})"
|
|
625
|
+
)
|
|
743
626
|
|
|
744
627
|
def test_make_admin_popup(self):
|
|
745
628
|
email, password = signup_teacher_directly()
|
|
746
629
|
school = create_organisation_directly(email)
|
|
747
|
-
page =
|
|
630
|
+
page = (
|
|
631
|
+
self.go_to_homepage()
|
|
632
|
+
.go_to_teacher_login_page()
|
|
633
|
+
.login(email, password)
|
|
634
|
+
)
|
|
748
635
|
joining_email, _ = signup_teacher_directly()
|
|
749
636
|
|
|
750
|
-
invite_data = {
|
|
637
|
+
invite_data = {
|
|
638
|
+
"teacher_first_name": "Real",
|
|
639
|
+
"teacher_last_name": "Name",
|
|
640
|
+
"teacher_email": "ren@me.me",
|
|
641
|
+
}
|
|
751
642
|
|
|
752
643
|
for key in invite_data.keys():
|
|
753
644
|
field = page.browser.find_element(By.NAME, key)
|
|
754
645
|
field.send_keys(invite_data[key])
|
|
755
646
|
|
|
756
|
-
invite_button = page.browser.find_element(
|
|
647
|
+
invite_button = page.browser.find_element(
|
|
648
|
+
By.NAME, "invite_teacher_button"
|
|
649
|
+
)
|
|
757
650
|
invite_button.click()
|
|
758
651
|
|
|
759
652
|
# Once invite sent test the make admin button
|
|
@@ -764,7 +657,11 @@ class TestTeacherFrontend(BaseTest):
|
|
|
764
657
|
make_admin_button.click()
|
|
765
658
|
"""
|
|
766
659
|
|
|
767
|
-
button_ids = [
|
|
660
|
+
button_ids = [
|
|
661
|
+
"make_admin_button_invite",
|
|
662
|
+
"cancel_admin_popup_button",
|
|
663
|
+
"delete-invite",
|
|
664
|
+
]
|
|
768
665
|
click_buttons_by_id(page, self, button_ids)
|
|
769
666
|
|
|
770
667
|
# Delete the invite and check if the form invite with
|
|
@@ -797,27 +694,40 @@ class TestTeacherFrontend(BaseTest):
|
|
|
797
694
|
create_organisation_directly(email)
|
|
798
695
|
|
|
799
696
|
self.selenium.get(self.live_server_url)
|
|
800
|
-
page =
|
|
697
|
+
page = (
|
|
698
|
+
HomePage(self.selenium)
|
|
699
|
+
.go_to_teacher_login_page()
|
|
700
|
+
.login(email, password)
|
|
701
|
+
.open_account_tab()
|
|
702
|
+
)
|
|
801
703
|
|
|
802
704
|
# test incorrect password
|
|
803
|
-
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
705
|
+
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
706
|
+
"IncorrectPassword"
|
|
707
|
+
)
|
|
804
708
|
page.browser.find_element(By.ID, "delete_account_button").click()
|
|
805
709
|
is_message_showing(page.browser, "Your account was not deleted")
|
|
806
710
|
|
|
807
711
|
# test cancel (no class)
|
|
808
712
|
time.sleep(FADE_TIME)
|
|
809
713
|
page.browser.find_element(By.ID, "id_delete_password").clear()
|
|
810
|
-
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
714
|
+
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
715
|
+
password
|
|
716
|
+
)
|
|
811
717
|
page.browser.find_element(By.ID, "delete_account_button").click()
|
|
812
718
|
|
|
813
719
|
time.sleep(FADE_TIME)
|
|
814
|
-
assert page.browser.find_element(
|
|
720
|
+
assert page.browser.find_element(
|
|
721
|
+
By.ID, "popup-delete-review"
|
|
722
|
+
).is_displayed()
|
|
815
723
|
page.browser.find_element(By.ID, "cancel_popup_button").click()
|
|
816
724
|
time.sleep(FADE_TIME)
|
|
817
725
|
|
|
818
726
|
# test close button in the corner
|
|
819
727
|
page.browser.find_element(By.ID, "id_delete_password").clear()
|
|
820
|
-
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
728
|
+
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
729
|
+
password
|
|
730
|
+
)
|
|
821
731
|
page.browser.find_element(By.ID, "delete_account_button").click()
|
|
822
732
|
|
|
823
733
|
time.sleep(FADE_TIME)
|
|
@@ -829,11 +739,15 @@ class TestTeacherFrontend(BaseTest):
|
|
|
829
739
|
create_school_student_directly(access_code)
|
|
830
740
|
|
|
831
741
|
# delete then review classes
|
|
832
|
-
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
742
|
+
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
743
|
+
password
|
|
744
|
+
)
|
|
833
745
|
page.browser.find_element(By.ID, "delete_account_button").click()
|
|
834
746
|
|
|
835
747
|
time.sleep(FADE_TIME)
|
|
836
|
-
assert page.browser.find_element(
|
|
748
|
+
assert page.browser.find_element(
|
|
749
|
+
By.ID, "popup-delete-review"
|
|
750
|
+
).is_displayed()
|
|
837
751
|
page.browser.find_element(By.ID, "review_button").click()
|
|
838
752
|
time.sleep(FADE_TIME)
|
|
839
753
|
|
|
@@ -841,7 +755,9 @@ class TestTeacherFrontend(BaseTest):
|
|
|
841
755
|
page = page.open_account_tab()
|
|
842
756
|
|
|
843
757
|
# test actual deletion
|
|
844
|
-
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
758
|
+
page.browser.find_element(By.ID, "id_delete_password").send_keys(
|
|
759
|
+
password
|
|
760
|
+
)
|
|
845
761
|
page.browser.find_element(By.ID, "delete_account_button").click()
|
|
846
762
|
|
|
847
763
|
time.sleep(FADE_TIME)
|
|
@@ -851,29 +767,49 @@ class TestTeacherFrontend(BaseTest):
|
|
|
851
767
|
assert page.browser.find_element(By.CLASS_NAME, "banner--homepage")
|
|
852
768
|
|
|
853
769
|
# user should not be able to login now
|
|
854
|
-
page =
|
|
770
|
+
page = (
|
|
771
|
+
HomePage(self.selenium)
|
|
772
|
+
.go_to_teacher_login_page()
|
|
773
|
+
.login_failure(email, password)
|
|
774
|
+
)
|
|
855
775
|
|
|
856
|
-
assert page.has_login_failed(
|
|
776
|
+
assert page.has_login_failed(
|
|
777
|
+
"form-login-teacher", INVALID_LOGIN_MESSAGE
|
|
778
|
+
)
|
|
857
779
|
|
|
858
780
|
def test_onboarding_complete(self):
|
|
859
781
|
email, password = signup_teacher_directly()
|
|
860
782
|
|
|
861
783
|
self.selenium.get(self.live_server_url)
|
|
862
|
-
page =
|
|
784
|
+
page = (
|
|
785
|
+
HomePage(self.selenium)
|
|
786
|
+
.go_to_teacher_login_page()
|
|
787
|
+
.login_no_school(email, password)
|
|
788
|
+
)
|
|
863
789
|
|
|
864
790
|
page = page.create_organisation("Test school", "W1", "GB")
|
|
865
791
|
page = page.create_class("Test class", True)
|
|
866
|
-
page =
|
|
792
|
+
page = (
|
|
793
|
+
page.type_student_name("Test Student")
|
|
794
|
+
.create_students()
|
|
795
|
+
.complete_setup()
|
|
796
|
+
)
|
|
867
797
|
|
|
868
798
|
assert page.has_onboarding_complete_popup()
|
|
869
799
|
|
|
870
800
|
def get_to_forgotten_password_page(self):
|
|
871
801
|
self.selenium.get(self.live_server_url)
|
|
872
|
-
page =
|
|
802
|
+
page = (
|
|
803
|
+
HomePage(self.selenium)
|
|
804
|
+
.go_to_teacher_login_page()
|
|
805
|
+
.go_to_teacher_forgotten_password_page()
|
|
806
|
+
)
|
|
873
807
|
return page
|
|
874
808
|
|
|
875
809
|
def wait_for_email(self):
|
|
876
|
-
WebDriverWait(self.selenium, 2).until(
|
|
810
|
+
WebDriverWait(self.selenium, 2).until(
|
|
811
|
+
lambda driver: len(mail.outbox) == 1
|
|
812
|
+
)
|
|
877
813
|
|
|
878
814
|
def is_dashboard_page(self, page):
|
|
879
815
|
return page.__class__.__name__ == "TeachDashboardPage"
|