rapid-router 5.18.0__py2.py3-none-any.whl → 7.6.8__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.
Files changed (170) hide show
  1. example_project/rapid_router_test_settings.py +19 -7
  2. example_project/settings.py +21 -8
  3. example_project/urls.py +5 -6
  4. game/__init__.py +1 -1
  5. game/admin.py +7 -2
  6. game/character.py +8 -0
  7. game/decor.py +40 -0
  8. game/end_to_end_tests/base_game_test.py +34 -27
  9. game/end_to_end_tests/editor_page.py +15 -0
  10. game/end_to_end_tests/game_page.py +88 -20
  11. game/end_to_end_tests/selenium_test_case.py +1 -20
  12. game/end_to_end_tests/test_cow_crashes.py +3 -5
  13. game/end_to_end_tests/test_level_editor.py +273 -10
  14. game/end_to_end_tests/test_level_selection.py +25 -3
  15. game/end_to_end_tests/test_play_through.py +222 -127
  16. game/end_to_end_tests/test_python_levels.py +41 -7
  17. game/end_to_end_tests/test_saving_workspace.py +2 -1
  18. game/forms.py +7 -1
  19. game/level_management.py +26 -11
  20. game/messages.py +899 -337
  21. game/migrations/0001_squashed_0025_levels_ordering_pt1.py +19 -1
  22. game/migrations/0026_levels_pt2.py +13 -2
  23. game/migrations/0032_cannot_turn_left_level.py +13 -2
  24. game/migrations/0033_recursion_level.py +13 -2
  25. game/migrations/0034_joes_level.py +13 -2
  26. game/migrations/0035_disable_route_score_level_70.py +0 -2
  27. game/migrations/0036_level_score_73.py +0 -2
  28. game/migrations/0037_level_score_79.py +0 -2
  29. game/migrations/0038_level_score_40.py +0 -1
  30. game/migrations/0042_level_score_73.py +0 -2
  31. game/migrations/0048_add_cow_field_and_blocks.py +0 -2
  32. game/migrations/0049_level_score_34.py +0 -2
  33. game/migrations/0050_level_score_40.py +0 -2
  34. game/migrations/0051_level_score_49.py +0 -1
  35. game/migrations/0086_loop_levels.py +13 -2
  36. game/migrations/0092_disable_algo_score_in_custom_levels.py +28 -0
  37. game/migrations/0093_alter_level_character_name.py +18 -0
  38. game/migrations/0094_add_hint_lesson_subtitle_to_levels.py +28 -0
  39. game/migrations/0095_level_commands.py +18 -0
  40. game/migrations/0096_alter_level_commands.py +18 -0
  41. game/migrations/0097_add_python_den_levels.py +1515 -0
  42. game/migrations/0098_add_episode_link_fields.py +44 -0
  43. game/migrations/0099_python_episodes_links.py +103 -0
  44. game/migrations/0100_reorder_python_levels.py +179 -0
  45. game/migrations/0101_rename_episodes.py +45 -0
  46. game/migrations/0102_reoder_episodes_13_14.py +136 -0
  47. game/migrations/0103_level_1015_solution.py +26 -0
  48. game/migrations/0104_remove_level_direct_drive.py +17 -0
  49. game/migrations/0105_delete_invalid_attempts.py +18 -0
  50. game/migrations/0106_fields_to_snake_case.py +48 -0
  51. game/migrations/0107_rename_worksheet_link_episode_student_worksheet_link.py +18 -0
  52. game/migrations/0108_episode_indy_worksheet_link.py +18 -0
  53. game/migrations/0109_create_episodes_23_and_24.py +99 -0
  54. game/migrations/0110_remove_episode_indy_worksheet_link_and_more.py +100 -0
  55. game/migrations/0111_create_worksheets.py +149 -0
  56. game/migrations/0112_worksheet_locked_classes.py +21 -0
  57. game/migrations/0113_level_needs_approval.py +18 -0
  58. game/migrations/0114_default_and_non_student_levels_no_approval.py +31 -0
  59. game/migrations/0115_level_level__default_does_not_need_approval.py +22 -0
  60. game/migrations/0116_update_worksheet_video_links.py +68 -0
  61. game/migrations/0117_update_solutions_to_if_else.py +61 -0
  62. game/models.py +127 -17
  63. game/permissions.py +51 -19
  64. game/python_den_urls.py +26 -0
  65. game/random_road.py +9 -9
  66. game/serializers.py +12 -17
  67. game/static/django_reverse_js/js/reverse.js +171 -0
  68. game/static/game/css/LilitaOne-Regular.ttf +0 -0
  69. game/static/game/css/backgrounds.css +8 -12
  70. game/static/game/css/dataTables.custom.css +3 -2
  71. game/static/game/css/editor.css +47 -0
  72. game/static/game/css/game.css +37 -43
  73. game/static/game/css/game_screen.css +16 -0
  74. game/static/game/css/level_editor.css +5 -0
  75. game/static/game/css/level_selection.css +17 -2
  76. game/static/game/image/Python_Den_hero_student.png +0 -0
  77. game/static/game/image/Python_levels_page.svg +1954 -0
  78. game/static/game/image/characters/front_view/Electric_van.svg +448 -0
  79. game/static/game/image/characters/top_view/Electric_van.svg +448 -0
  80. game/static/game/image/decor/city/solar_panel.svg +1200 -0
  81. game/static/game/image/decor/farm/solar_panel.svg +86 -0
  82. game/static/game/image/decor/grass/solar_panel.svg +86 -0
  83. game/static/game/image/decor/snow/solar_panel.svg +173 -0
  84. game/static/game/image/electric_van.svg +448 -0
  85. game/static/game/image/icons/description.svg +1 -0
  86. game/static/game/image/icons/hint.svg +1 -0
  87. game/static/game/image/icons/python.svg +1 -1
  88. game/static/game/image/pigeon.svg +684 -0
  89. game/static/game/image/python_den_header.svg +19 -0
  90. game/static/game/js/animation.js +65 -24
  91. game/static/game/js/blockly/msg/js/bg.js +52 -1
  92. game/static/game/js/blockly/msg/js/ca.js +52 -1
  93. game/static/game/js/blockly/msg/js/en-gb.js +2 -0
  94. game/static/game/js/blockly/msg/js/en.js +2 -0
  95. game/static/game/js/blockly/msg/js/es.js +52 -1
  96. game/static/game/js/blockly/msg/js/fr.js +2 -0
  97. game/static/game/js/blockly/msg/js/hi.js +2 -0
  98. game/static/game/js/blockly/msg/js/it.js +52 -1
  99. game/static/game/js/blockly/msg/js/pl.js +52 -1
  100. game/static/game/js/blockly/msg/js/pt-br.js +52 -1
  101. game/static/game/js/blockly/msg/js/ru.js +52 -1
  102. game/static/game/js/blockly/msg/js/ur.js +52 -1
  103. game/static/game/js/blocklyCustomBlocks.js +93 -52
  104. game/static/game/js/button.js +12 -0
  105. game/static/game/js/cow.js +11 -7
  106. game/static/game/js/drawing.js +68 -29
  107. game/static/game/js/editor.js +23 -0
  108. game/static/game/js/game.js +74 -110
  109. game/static/game/js/level_editor.js +646 -274
  110. game/static/game/js/level_moderation.js +33 -2
  111. game/static/game/js/level_selection.js +1 -1
  112. game/static/game/js/loadLanguages.js +2 -2
  113. game/static/game/js/model.js +32 -2
  114. game/static/game/js/pythonControl.js +14 -1
  115. game/static/game/js/scoreboard.js +0 -37
  116. game/static/game/js/scoreboardSharedLevels.js +48 -0
  117. game/static/game/js/skulpt/skulpt-stdlib.js +1 -1
  118. game/static/game/js/sound.js +52 -5
  119. game/static/game/raphael_image/characters/top_view/Electric_van.svg +448 -0
  120. game/static/game/raphael_image/decor/city/solar_panel.svg +1200 -0
  121. game/static/game/raphael_image/decor/farm/solar_panel.svg +86 -0
  122. game/static/game/raphael_image/decor/grass/solar_panel.svg +86 -0
  123. game/static/game/raphael_image/decor/snow/solar_panel.svg +173 -0
  124. game/static/game/raphael_image/pigeon.svg +685 -0
  125. game/static/game/sass/game.scss +2 -2
  126. game/static/game/sound/clown_horn.mp3 +0 -0
  127. game/static/game/sound/clown_horn.ogg +0 -0
  128. game/static/game/sound/electric_van_starting.mp3 +0 -0
  129. game/static/game/sound/electric_van_starting.ogg +0 -0
  130. game/static/game/sound/pigeon.mp3 +0 -0
  131. game/static/game/sound/pigeon.ogg +0 -0
  132. game/static/game/sound/sleigh_bells.mp3 +0 -0
  133. game/static/game/sound/sleigh_bells.ogg +0 -0
  134. game/static/game/sound/sleigh_crash.mp3 +0 -0
  135. game/static/game/sound/sleigh_crash.ogg +0 -0
  136. game/templates/game/base.html +34 -14
  137. game/templates/game/basenonav.html +11 -5
  138. game/templates/game/game.html +142 -38
  139. game/templates/game/level_editor.html +340 -236
  140. game/templates/game/level_moderation.html +19 -6
  141. game/templates/game/level_selection.html +18 -110
  142. game/templates/game/python_den_level_selection.html +291 -0
  143. game/templates/game/python_den_worksheet.html +101 -0
  144. game/templates/game/scoreboard.html +83 -64
  145. game/tests/test_level_editor.py +94 -26
  146. game/tests/test_level_selection.py +149 -46
  147. game/tests/test_python_den_worksheet.py +85 -0
  148. game/tests/test_scoreboard.py +34 -7
  149. game/tests/utils/level.py +32 -26
  150. game/theme.py +5 -5
  151. game/urls.py +199 -61
  152. game/views/language_code_conversions.py +86 -86
  153. game/views/level.py +155 -63
  154. game/views/level_editor.py +88 -55
  155. game/views/level_moderation.py +23 -0
  156. game/views/level_selection.py +116 -47
  157. game/views/level_solutions.py +491 -106
  158. game/views/scoreboard.py +76 -51
  159. game/views/worksheet.py +25 -0
  160. rapid_router-7.6.8.dist-info/METADATA +174 -0
  161. {rapid_router-5.18.0.dist-info → rapid_router-7.6.8.dist-info}/RECORD +164 -104
  162. {rapid_router-5.18.0.dist-info → rapid_router-7.6.8.dist-info}/WHEEL +1 -1
  163. example_project/manage.py +0 -10
  164. game/static/game/image/actions/go.svg +0 -18
  165. game/static/game/js/js-reverse.js +0 -14
  166. game/static/game/js/pqselect.min.js +0 -9
  167. game/static/game/js/widget-scroller.js +0 -906
  168. rapid_router-5.18.0.dist-info/METADATA +0 -17
  169. {rapid_router-5.18.0.dist-info → rapid_router-7.6.8.dist-info/licenses}/LICENSE.md +0 -0
  170. {rapid_router-5.18.0.dist-info → rapid_router-7.6.8.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,37 @@
1
1
  from selenium.webdriver.common.action_chains import ActionChains
2
2
  from selenium.webdriver.common.by import By
3
+ from selenium.webdriver.support import expected_conditions as EC
4
+ from selenium.webdriver.support.ui import Select, WebDriverWait
3
5
 
4
6
  from game.end_to_end_tests.base_game_test import BaseGameTest
5
7
  from game.views.level_editor import available_blocks
6
8
 
9
+ DELAY_TIME = 10
10
+
7
11
 
8
12
  class TestLevelEditor(BaseGameTest):
13
+ def set_up_basic_map(self):
14
+ self.test_level_editor_displays()
15
+
16
+ add_road_button = self.selenium.find_element(By.ID, "add_road")
17
+ add_road_button.click()
18
+
19
+ road_start = self.selenium.find_element(
20
+ By.CSS_SELECTOR, "rect[x='130'][y='530']"
21
+ )
22
+ road_end = self.selenium.find_element(By.CSS_SELECTOR, "rect[x='330'][y='530']")
23
+ ActionChains(self.selenium).drag_and_drop(road_start, road_end).perform()
24
+
25
+ mark_start_button = self.selenium.find_element(By.ID, "start")
26
+ mark_start_button.click()
27
+ ActionChains(self.selenium).move_to_element(road_start).click().perform()
28
+
29
+ add_house_button = self.selenium.find_element(By.ID, "add_house")
30
+ add_house_button.click()
31
+ ActionChains(self.selenium).move_to_element(road_end).click().perform()
32
+
33
+ return [road_start, road_end]
34
+
9
35
  def test_level_editor_displays(self):
10
36
  page = self.go_to_level_editor()
11
37
 
@@ -22,25 +48,262 @@ class TestLevelEditor(BaseGameTest):
22
48
  assert page.element_exists_by_id(f"{block_type}_image")
23
49
 
24
50
  def test_multiple_houses(self):
25
- add_road_button = self.selenium.find_element(By.ID, "add_road")
26
- add_road_button.click()
51
+ [road_start, road_end] = self.set_up_basic_map()
27
52
 
28
- road_start = self.selenium.find_element(By.CSS_SELECTOR, "rect[x='130'][y='530']")
29
- road_end = self.selenium.find_element(By.CSS_SELECTOR, "rect[x='330'][y='530']")
30
- ActionChains(self.selenium).drag_and_drop(road_start, road_end).perform()
53
+ road_middle = self.selenium.find_element(
54
+ By.CSS_SELECTOR, "rect[x='230'][y='530']"
55
+ )
31
56
 
32
57
  add_house_button = self.selenium.find_element(By.ID, "add_house")
33
58
  add_house_button.click()
34
- ActionChains(self.selenium).move_to_element(road_start).click().perform()
35
- ActionChains(self.selenium).move_to_element(road_end).click().perform()
59
+ ActionChains(self.selenium).move_to_element(road_middle).click().perform()
36
60
 
37
- added_houses = self.selenium.find_elements(By.CSS_SELECTOR, "rect[fill='#0000ff']")
61
+ added_houses = self.selenium.find_elements(
62
+ By.CSS_SELECTOR, "rect[fill='#0000ff']"
63
+ )
38
64
  assert len(added_houses) == 2
39
65
 
40
66
  delete_house_button = self.selenium.find_element(By.ID, "delete_house")
41
67
  delete_house_button.click()
42
- ActionChains(self.selenium).move_to_element(road_end).click().perform()
68
+ ActionChains(self.selenium).move_to_element(road_middle).click().perform()
43
69
  ActionChains(self.selenium).move_to_element(road_start).perform()
44
70
 
45
- houses_after_delete = self.selenium.find_elements(By.CSS_SELECTOR, "rect[fill='#0000ff']")
71
+ houses_after_delete = self.selenium.find_elements(
72
+ By.CSS_SELECTOR, "rect[fill='#0000ff']"
73
+ )
46
74
  assert len(houses_after_delete) == 1
75
+
76
+ def test_cow_on_origin(self):
77
+ page = self.go_to_level_editor()
78
+ [road_start, road_end] = self.set_up_basic_map()
79
+
80
+ origin_space = self.selenium.find_elements(
81
+ By.CSS_SELECTOR, "rect[fill='#ff0000']"
82
+ )
83
+ assert len(origin_space) == 1
84
+
85
+ page.go_to_scenery_tab()
86
+
87
+ draggable_cow = self.selenium.find_element(By.ID, "cow")
88
+ ActionChains(self.selenium).click_and_hold(draggable_cow).move_to_element(
89
+ road_start
90
+ ).perform()
91
+ start_space_warning = self.selenium.find_elements(
92
+ By.CSS_SELECTOR,
93
+ "rect[fill='#e35f4d'][fill-opacity='0.7'][x='130'][y='530']",
94
+ )
95
+ assert len(start_space_warning) == 1
96
+
97
+ def test_cow_on_house(self):
98
+ page = self.go_to_level_editor()
99
+ [road_start, road_end] = self.set_up_basic_map()
100
+
101
+ house_space = self.selenium.find_elements(
102
+ By.CSS_SELECTOR, "rect[fill='#0000ff']"
103
+ )
104
+ assert len(house_space) == 1
105
+ assert road_end == house_space[0]
106
+
107
+ page.go_to_scenery_tab()
108
+
109
+ draggable_cow = self.selenium.find_elements(By.ID, "cow")
110
+ assert len(draggable_cow) == 1
111
+ ActionChains(self.selenium).click_and_hold(draggable_cow[0]).move_to_element(
112
+ road_end
113
+ ).perform()
114
+ allowed_space = self.selenium.find_elements(
115
+ By.CSS_SELECTOR, "rect[fill='#87e34d']"
116
+ )
117
+ assert len(allowed_space) == 0
118
+
119
+ def test_draggable_decor(self):
120
+ page = self.go_to_level_editor()
121
+ page.go_to_scenery_tab()
122
+
123
+ source_tree = self.selenium.find_element(By.ID, "tree2")
124
+ end_space = self.selenium.find_element(
125
+ By.CSS_SELECTOR, "rect[x='130'][y='530']"
126
+ )
127
+ ActionChains(self.selenium).drag_and_drop(source_tree, end_space).perform()
128
+
129
+ decor_tree = self.selenium.find_elements(By.CSS_SELECTOR, "image[x='0'][y='0']")
130
+ cloned_source_tree = self.selenium.find_elements(By.ID, "tree2")
131
+ assert len(decor_tree) == 1
132
+ assert len(cloned_source_tree) == 1
133
+
134
+ def test_draggable_cow(self):
135
+ page = self.go_to_level_editor()
136
+ page.go_to_scenery_tab()
137
+
138
+ source_cow = self.selenium.find_element(By.ID, "cow")
139
+ end_space = self.selenium.find_element(
140
+ By.CSS_SELECTOR, "rect[x='130'][y='530']"
141
+ )
142
+ ActionChains(self.selenium).drag_and_drop(source_cow, end_space).perform()
143
+
144
+ scenery_cow = self.selenium.find_elements(
145
+ By.CSS_SELECTOR, "image[x='0'][y='0']"
146
+ )
147
+ cloned_source_cow = self.selenium.find_elements(By.ID, "cow")
148
+ assert len(scenery_cow) == 1
149
+ assert len(cloned_source_cow) == 1
150
+
151
+ def test_draggable_traffic_light(self):
152
+ page = self.go_to_level_editor()
153
+ page.go_to_scenery_tab()
154
+
155
+ source_light = self.selenium.find_element(By.ID, "trafficLightRed")
156
+ end_space = self.selenium.find_element(
157
+ By.CSS_SELECTOR, "rect[x='130'][y='530']"
158
+ )
159
+ ActionChains(self.selenium).drag_and_drop(source_light, end_space).perform()
160
+
161
+ scenery_light = self.selenium.find_elements(
162
+ By.CSS_SELECTOR, "image[x='0'][y='0']"
163
+ )
164
+ cloned_source_light = self.selenium.find_elements(By.ID, "trafficLightRed")
165
+ assert len(scenery_light) == 1
166
+ assert len(cloned_source_light) == 1
167
+
168
+ def test_custom_description_and_hint(self):
169
+ # login
170
+ self.login_once()
171
+
172
+ # go to level editor and set up basic map
173
+ page = self.go_to_level_editor()
174
+ [road_start, road_end] = self.set_up_basic_map()
175
+
176
+ # fill in custom description and hint fields
177
+ page.go_to_description_tab()
178
+ self.selenium.find_element(By.ID, "subtitle").send_keys("test subtitle")
179
+ self.selenium.find_element(By.ID, "description").send_keys("test description")
180
+
181
+ page.go_to_hint_tab()
182
+ self.selenium.find_element(By.ID, "hint").send_keys("test hint")
183
+
184
+ # save level and choose to play it
185
+ page.go_to_save_tab()
186
+ self.selenium.find_element(By.ID, "levelNameInput").send_keys("test level")
187
+ self.selenium.find_element(By.ID, "saveLevel").click()
188
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
189
+ EC.presence_of_element_located((By.ID, "play_button"))
190
+ )
191
+ self.selenium.find_element(By.ID, "play_button").click()
192
+
193
+ # check to see if custom subtitle and lesson appear on the initial popup
194
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
195
+ EC.presence_of_element_located((By.ID, "myModal-lead"))
196
+ )
197
+ modal_text = self.selenium.find_element(By.ID, "myModal-lead").get_attribute(
198
+ "innerHTML"
199
+ )
200
+ assert "test subtitle" in modal_text
201
+ assert "test description" in modal_text
202
+ self.selenium.find_element(By.ID, "close-modal").click()
203
+
204
+ # wait for modal to disappear
205
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
206
+ EC.none_of(
207
+ EC.visibility_of_all_elements_located((By.ID, "myModal-mainText"))
208
+ )
209
+ )
210
+
211
+ # check to see if the custom hint appears on failure modal
212
+ fast_tab = self.selenium.find_element(By.ID, "fast_tab")
213
+ fast_tab.click()
214
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
215
+ EC.visibility_of_element_located((By.ID, "hintPopupBtn"))
216
+ )
217
+ hint_button = self.selenium.find_element(By.ID, "hintPopupBtn")
218
+ hint_button.click()
219
+
220
+ hint_modal_text = self.selenium.find_element(By.ID, "hintText").get_attribute(
221
+ "innerHTML"
222
+ )
223
+ hint_modal_style = self.selenium.find_element(By.ID, "hintText").get_attribute(
224
+ "style"
225
+ )
226
+ assert "display: none" in hint_button.get_attribute("style")
227
+ assert "display: block" in hint_modal_style
228
+ assert "test hint" in hint_modal_text
229
+
230
+ self.selenium.find_element(By.ID, "try_again_button").click()
231
+
232
+ # check to see if the custom hint appears on the hint popup
233
+ self.selenium.find_element(By.ID, "help_tab").click()
234
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
235
+ EC.visibility_of_element_located((By.ID, "myModal-mainText"))
236
+ )
237
+ hint_modal_text_two = self.selenium.find_element(
238
+ By.ID, "myModal-mainText"
239
+ ).get_attribute("innerHTML")
240
+ assert "test hint" in hint_modal_text_two
241
+
242
+ def test_solar_panels(self):
243
+ """test that the solar panels appear as a scenery option when clicking on the scenery tab
244
+ """
245
+ page = self.go_to_level_editor()
246
+ page.go_to_scenery_tab()
247
+
248
+ solar_panel = self.selenium.find_element(
249
+ By.ID, "solar_panel"
250
+ )
251
+ assert solar_panel.is_displayed()
252
+
253
+ def test_electric_fuel_gauge(self):
254
+ self.login_once()
255
+
256
+ page = self.go_to_level_editor()
257
+
258
+ [road_start, road_end] = self.set_up_basic_map()
259
+ page.go_to_character_tab()
260
+
261
+ # the electric van has dropdown value 7 - select the electric van as character
262
+ Select(self.selenium.find_element(By.ID, "character_select")).select_by_value(
263
+ "7"
264
+ )
265
+
266
+ # save level and choose to play it
267
+ page.go_to_save_tab()
268
+ self.selenium.find_element(By.ID, "levelNameInput").send_keys(
269
+ "test electric van level"
270
+ )
271
+ self.selenium.find_element(By.ID, "saveLevel").click()
272
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
273
+ EC.visibility_of_element_located((By.ID, "myModal-lead"))
274
+ )
275
+ self.selenium.find_element(By.ID, "play_button").click()
276
+
277
+ # check to see if electric fuel gauge appears
278
+ assert WebDriverWait(self.selenium, DELAY_TIME).until(
279
+ EC.presence_of_element_located((By.ID, "electricFuelGauge"))
280
+ )
281
+ electric_fuel_gauge = self.selenium.find_element(By.ID, "electricFuelGauge")
282
+ assert "visibility: visible" in electric_fuel_gauge.get_attribute("style")
283
+
284
+ def test_pigeon(self):
285
+ """Test that cows on the map automatically become pigeons when the theme is changed to "city" """
286
+ page = self.go_to_level_editor()
287
+ page.go_to_scenery_tab()
288
+
289
+ source_cow = self.selenium.find_element(By.ID, "cow")
290
+ end_space = self.selenium.find_element(
291
+ By.CSS_SELECTOR, "rect[x='130'][y='530']"
292
+ )
293
+ ActionChains(self.selenium).drag_and_drop(source_cow, end_space).perform()
294
+
295
+ scenery_cow = self.selenium.find_elements(
296
+ By.CSS_SELECTOR, "image[x='0'][y='0']"
297
+ )
298
+ cow_link = scenery_cow[0].get_attribute("href")
299
+ assert cow_link == "/static/game/raphael_image/Clarice.svg"
300
+
301
+ Select(self.selenium.find_element(By.ID, "theme_select")).select_by_value(
302
+ "city"
303
+ )
304
+
305
+ scenery_pigeon = self.selenium.find_elements(
306
+ By.CSS_SELECTOR, "image[x='0'][y='0']"
307
+ )
308
+ pigeon_link = scenery_pigeon[0].get_attribute("href")
309
+ assert pigeon_link == "/static/game/raphael_image/pigeon.svg"
@@ -1,3 +1,4 @@
1
+ import pytest
1
2
  from common.tests.utils.classes import create_class_directly
2
3
  from common.tests.utils.organisation import create_organisation_directly
3
4
  from common.tests.utils.student import create_school_student_directly
@@ -34,7 +35,9 @@ class TestLevelSelection(BaseGameTest):
34
35
  page = self.go_to_reverse("levels")
35
36
 
36
37
  # The coin images for the levels
37
- level_coin_images = page.browser.find_elements(By.CSS_SELECTOR, ("#collapse-4 div img"))
38
+ level_coin_images = page.browser.find_elements(
39
+ By.CSS_SELECTOR, ("#collapse-4 div img")
40
+ )
38
41
  # There are 4 levels in this episode, each with gold coin
39
42
  assert_that(len(level_coin_images), equal_to(4))
40
43
 
@@ -45,13 +48,32 @@ class TestLevelSelection(BaseGameTest):
45
48
  )
46
49
 
47
50
  # So the episode has a gold coin too
48
- episode_coin_image = page.browser.find_element(By.CSS_SELECTOR, "#episode-4 > p > img")
51
+ episode_coin_image = page.browser.find_element(
52
+ By.CSS_SELECTOR, "#episode-4 > p > img"
53
+ )
49
54
  assert_that(
50
55
  episode_coin_image.get_attribute("src"),
51
56
  ends_with("/static/game/image/coins/coin_gold.svg"),
52
57
  )
53
58
 
54
59
  try:
55
- image_for_uncomplete_episode = page.browser.find_element(By.CSS_SELECTOR, "#episode-3 > p > img")
60
+ image_for_uncomplete_episode = page.browser.find_element(
61
+ By.CSS_SELECTOR, "#episode-3 > p > img"
62
+ )
56
63
  except NoSuchElementException as this_should_happen:
57
64
  pass
65
+
66
+ @pytest.mark.skip(reason="levelless episode no longer opens up after clicking next level, need to investigate")
67
+ def test_redirect_to_levelless_episode(self):
68
+ levels_page = self.go_to_reverse("python_levels")
69
+ expected_url = levels_page.browser.current_url
70
+
71
+ page = self.go_to_level_without_dismissing_dialog(26, True)
72
+ self.selenium.find_element(By.ID, "next_button").click()
73
+
74
+ current_url = page.browser.current_url
75
+ assert current_url == expected_url
76
+
77
+ episode_20_header = self.selenium.find_element(By.ID, "collapse-20")
78
+ episode_20_expanded = episode_20_header.get_attribute("class")
79
+ assert episode_20_expanded == "collapse"