problem-editor.py 12.5 KB
Newer Older
cahrens committed
1
# disable missing docstring
2
# pylint: disable=missing-docstring
cahrens committed
3

4
import json
cahrens committed
5
from lettuce import world, step
6
from nose.tools import assert_equal, assert_true  # pylint: disable=no-name-in-module
Julian Arni committed
7
from common import type_in_codemirror, open_new_course
8
from advanced_settings import change_value, ADVANCED_MODULES_KEY
9
from course_import import import_file
10

cahrens committed
11
DISPLAY_NAME = "Display Name"
12 13 14 15
MAXIMUM_ATTEMPTS = "Maximum Attempts"
PROBLEM_WEIGHT = "Problem Weight"
RANDOMIZATION = 'Randomization'
SHOW_ANSWER = "Show Answer"
16
SHOW_RESET_BUTTON = "Show Reset Button"
Nick Parlante committed
17
TIMER_BETWEEN_ATTEMPTS = "Timer Between Attempts"
18
MATLAB_API_KEY = "Matlab API key"
cahrens committed
19

20

cahrens committed
21 22
@step('I have created a Blank Common Problem$')
def i_created_blank_common_problem(step):
23
    step.given('I am in Studio editing a new unit')
24 25 26
    step.given("I have created another Blank Common Problem")


27 28
@step('I have created a unit with advanced module "(.*)"$')
def i_created_unit_with_advanced_module(step, advanced_module):
29
    step.given('I am in Studio editing a new unit')
30 31 32

    url = world.browser.url
    step.given("I select the Advanced Settings")
33
    change_value(step, ADVANCED_MODULES_KEY, '["{}"]'.format(advanced_module))
34 35 36 37 38 39 40 41 42 43 44 45 46 47
    world.visit(url)
    world.wait_for_xmodule()


@step('I have created an advanced component "(.*)" of type "(.*)"')
def i_create_new_advanced_component(step, component_type, advanced_component):
    world.create_component_instance(
        step=step,
        category='advanced',
        component_type=component_type,
        advanced_component=advanced_component
    )


48 49
@step('I have created another Blank Common Problem$')
def i_create_new_common_problem(step):
50
    world.create_component_instance(
51 52 53
        step=step,
        category='problem',
        component_type='Blank Common Problem'
54
    )
55

cahrens committed
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
@step('when I mouseover on "(.*)"')
def i_mouseover_on_html_component(step, element_class):
    action_css = '.{}'.format(element_class)
    world.trigger_event(action_css, event='mouseover')


@step(u'I can see Reply to Annotation link$')
def i_see_reply_to_annotation_link(_step):
    css_selector = 'a.annotatable-reply'
    world.wait_for_visible(css_selector)


@step(u'I see that page has scrolled "(.*)" when I click on "(.*)" link$')
def i_see_annotation_problem_page_scrolls(_step, scroll_direction, link_css):
    scroll_js = "$(window).scrollTop();"
    scroll_height_before = world.browser.evaluate_script(scroll_js)
    world.css_click("a.{}".format(link_css))
    scroll_height_after = world.browser.evaluate_script(scroll_js)
    if scroll_direction == "up":
        assert scroll_height_after < scroll_height_before
    elif scroll_direction == "down":
        assert scroll_height_after > scroll_height_before


@step('I have created an advanced problem of type "(.*)"$')
def i_create_new_advanced_problem(step, component_type):
    world.create_component_instance(
        step=step,
        category='problem',
        component_type=component_type,
        is_advanced=True
    )


cahrens committed
91
@step('I edit and select Settings$')
92
def i_edit_and_select_settings(_step):
cahrens committed
93 94
    world.edit_component_and_select_settings()

95

96 97
@step('I see the advanced settings and their expected values$')
def i_see_advanced_settings_with_values(step):
cahrens committed
98 99
    world.verify_all_setting_entries(
        [
100
            [DISPLAY_NAME, "Blank Common Problem", True],
101
            [MATLAB_API_KEY, "", False],
cahrens committed
102 103
            [MAXIMUM_ATTEMPTS, "", False],
            [PROBLEM_WEIGHT, "", False],
104
            [RANDOMIZATION, "Never", False],
105
            [SHOW_ANSWER, "Finished", False],
106
            [SHOW_RESET_BUTTON, "False", False],
107
            [TIMER_BETWEEN_ATTEMPTS, "0", False],
cahrens committed
108 109
        ])

110

cahrens committed
111
@step('I can modify the display name')
112
def i_can_modify_the_display_name(_step):
113 114
    # Verifying that the display name can be a string containing a floating point value
    # (to confirm that we don't throw an error because it is of the wrong type).
115
    index = world.get_setting_entry_index(DISPLAY_NAME)
116
    world.set_field_value(index, '3.4')
cahrens committed
117 118
    verify_modified_display_name()

119

cahrens committed
120 121 122 123 124
@step('my display name change is persisted on save')
def my_display_name_change_is_persisted_on_save(step):
    world.save_component_and_reopen(step)
    verify_modified_display_name()

125

cahrens committed
126 127 128 129 130
@step('the problem display name is "(.*)"$')
def verify_problem_display_name(step, name):
    assert_equal(name.upper(), world.browser.find_by_css('.problem-header').text)


131
@step('I can specify special characters in the display name')
132
def i_can_modify_the_display_name_with_special_chars(_step):
133
    index = world.get_setting_entry_index(DISPLAY_NAME)
134
    world.set_field_value(index, "updated ' \" &")
135 136
    verify_modified_display_name_with_special_chars()

137

138 139 140 141 142 143 144 145 146 147 148 149
@step('I can specify html in the display name and save')
def i_can_modify_the_display_name_with_html(_step):
    """
    If alert appear on save then UnexpectedAlertPresentException
    will occur and test will fail.
    """
    index = world.get_setting_entry_index(DISPLAY_NAME)
    world.set_field_value(index, "<script>alert('test')</script>")
    verify_modified_display_name_with_html()
    world.save_component()


150 151 152 153 154
@step('my special characters and persisted on save')
def special_chars_persisted_on_save(step):
    world.save_component_and_reopen(step)
    verify_modified_display_name_with_special_chars()

155

cahrens committed
156
@step('I can revert the display name to unset')
157
def can_revert_display_name_to_unset(_step):
cahrens committed
158 159 160
    world.revert_setting_entry(DISPLAY_NAME)
    verify_unset_display_name()

161

cahrens committed
162 163 164 165 166
@step('my display name is unset on save')
def my_display_name_is_persisted_on_save(step):
    world.save_component_and_reopen(step)
    verify_unset_display_name()

167

cahrens committed
168
@step('I can select Per Student for Randomization')
169
def i_can_select_per_student_for_randomization(_step):
cahrens committed
170 171 172
    world.browser.select(RANDOMIZATION, "Per Student")
    verify_modified_randomization()

173

cahrens committed
174 175 176 177 178
@step('my change to randomization is persisted')
def my_change_to_randomization_is_persisted(step):
    world.save_component_and_reopen(step)
    verify_modified_randomization()

179

cahrens committed
180 181 182 183
@step('I can revert to the default value for randomization')
def i_can_revert_to_default_for_randomization(step):
    world.revert_setting_entry(RANDOMIZATION)
    world.save_component_and_reopen(step)
184
    world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Never", False)
185

cahrens committed
186

187
@step('I can set the weight to "(.*)"?')
188
def i_can_set_weight(_step, weight):
189
    set_weight(weight)
cahrens committed
190 191
    verify_modified_weight()

192

cahrens committed
193
@step('my change to weight is persisted')
194
def my_change_to_weight_is_persisted(step):
cahrens committed
195 196 197
    world.save_component_and_reopen(step)
    verify_modified_weight()

198

cahrens committed
199
@step('I can revert to the default value of unset for weight')
200
def i_can_revert_to_default_for_unset_weight(step):
cahrens committed
201 202
    world.revert_setting_entry(PROBLEM_WEIGHT)
    world.save_component_and_reopen(step)
203 204
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)

cahrens committed
205

206 207 208
@step('if I set the weight to "(.*)", it remains unset')
def set_the_weight_to_abc(step, bad_weight):
    set_weight(bad_weight)
cahrens committed
209
    # We show the clear button immediately on type, hence the "True" here.
210
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True)
cahrens committed
211 212
    world.save_component_and_reopen(step)
    # But no change was actually ever sent to the model, so on reopen, explicitly_set is False
213
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
214

cahrens committed
215

216 217
@step('if I set the max attempts to "(.*)", it will persist as a valid integer$')
def set_the_max_attempts(step, max_attempts_set):
polesye committed
218
    # on firefox with selenium, the behavior is different.
219
    # eg 2.34 displays as 2.34 and is persisted as 2
220
    index = world.get_setting_entry_index(MAXIMUM_ATTEMPTS)
221
    world.set_field_value(index, max_attempts_set)
cahrens committed
222
    world.save_component_and_reopen(step)
223 224 225
    value = world.css_value('input.setting-input', index=index)
    assert value != "", "max attempts is blank"
    assert int(value) >= 0
226

cahrens committed
227 228 229

@step('Edit High Level Source is not visible')
def edit_high_level_source_not_visible(step):
230
    verify_high_level_source_links(step, False)
cahrens committed
231

232

cahrens committed
233
@step('Edit High Level Source is visible')
234 235
def edit_high_level_source_links_visible(step):
    verify_high_level_source_links(step, True)
cahrens committed
236

237

cahrens committed
238 239 240 241
@step('If I press Cancel my changes are not persisted')
def cancel_does_not_save_changes(step):
    world.cancel_component(step)
    step.given("I edit and select Settings")
242
    step.given("I see the advanced settings and their expected values")
cahrens committed
243

244

245 246 247 248
@step('I have enabled latex compiler')
def enable_latex_compiler(step):
    url = world.browser.url
    step.given("I select the Advanced Settings")
249
    change_value(step, 'Enable LaTeX Compiler', 'true')
250 251 252 253
    world.visit(url)
    world.wait_for_xmodule()


cahrens committed
254 255
@step('I have created a LaTeX Problem')
def create_latex_problem(step):
256
    step.given('I am in Studio editing a new unit')
257
    step.given('I have enabled latex compiler')
258 259 260 261 262 263
    world.create_component_instance(
        step=step,
        category='problem',
        component_type='Problem Written in LaTeX',
        is_advanced=True
    )
cahrens committed
264

265

266
@step('I edit and compile the High Level Source')
267
def edit_latex_source(_step):
268
    open_high_level_source()
269 270 271 272 273
    type_in_codemirror(1, "hi")
    world.css_click('.hls-compile')


@step('my change to the High Level Source is persisted')
274
def high_level_source_persisted(_step):
275
    def verify_text(driver):
Will Daly committed
276 277
        css_sel = '.problem div>span'
        return world.css_text(css_sel) == 'hi'
278

279
    world.wait_for(verify_text, timeout=10)
280 281 282


@step('I view the High Level Source I see my changes')
283
def high_level_source_in_editor(_step):
284
    open_high_level_source()
285
    assert_equal('hi', world.css_value('.source-edit-box'))
286

Julian Arni committed
287

288 289
@step(u'I have an empty course')
def i_have_empty_course(step):
Julian Arni committed
290
    open_new_course()
291

Julian Arni committed
292

293 294
@step(u'I import the file "([^"]*)"$')
def i_import_the_file(_step, filename):
Julian Arni committed
295
    import_file(filename)
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310


@step(u'I go to the vertical "([^"]*)"$')
def i_go_to_vertical(_step, vertical):
    world.css_click("span:contains('{0}')".format(vertical))


@step(u'I go to the unit "([^"]*)"$')
def i_go_to_unit(_step, unit):
    loc = "window.location = $(\"span:contains('{0}')\").closest('a').attr('href')".format(unit)
    world.browser.execute_script(loc)


@step(u'I see a message that says "([^"]*)"$')
def i_can_see_message(_step, msg):
Julian Arni committed
311
    msg = json.dumps(msg)     # escape quotes
Julian Arni committed
312
    world.css_has_text("h2.title", msg)
313 314 315 316


@step(u'I can edit the problem$')
def i_can_edit_problem(_step):
Julian Arni committed
317
    world.edit_component()
318

319

320 321
@step(u'I edit first blank advanced problem for annotation response$')
def i_edit_blank_problem_for_annotation_response(_step):
322
    world.edit_component(1)
323 324 325 326 327 328 329 330 331 332
    text = """
        <problem>
            <annotationresponse>
                <annotationinput><text>Text of annotation</text></annotationinput>
            </annotationresponse>
        </problem>"""
    type_in_codemirror(0, text)
    world.save_component()


333 334 335 336 337 338 339
@step(u'I can see cheatsheet$')
def verify_cheat_sheet_displaying(_step):
    world.css_click("a.cheatsheet-toggle")
    css_selector = 'article.simple-editor-cheatsheet'
    world.wait_for_visible(css_selector)


340
def verify_high_level_source_links(step, visible):
341
    if visible:
342 343
        assert_true(world.is_css_present('.launch-latex-compiler'),
                    msg="Expected to find the latex button but it is not present.")
344
    else:
345 346 347
        assert_true(world.is_css_not_present('.launch-latex-compiler'),
                    msg="Expected not to find the latex button but it is present.")

cahrens committed
348 349
    world.cancel_component(step)

350

cahrens committed
351
def verify_modified_weight():
352 353
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "3.5", True)

cahrens committed
354 355

def verify_modified_randomization():
356 357
    world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Per Student", True)

cahrens committed
358 359

def verify_modified_display_name():
360
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '3.4', True)
cahrens committed
361

362

363 364 365
def verify_modified_display_name_with_special_chars():
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, "updated ' \" &", True)

366

367 368 369 370
def verify_modified_display_name_with_html():
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, "<script>alert('test')</script>", True)


cahrens committed
371
def verify_unset_display_name():
372
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, 'Blank Advanced Problem', False)
373 374 375


def set_weight(weight):
376
    index = world.get_setting_entry_index(PROBLEM_WEIGHT)
377
    world.set_field_value(index, weight)
378 379 380


def open_high_level_source():
381
    world.edit_component()
382
    world.css_click('.launch-latex-compiler > a')