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

4
import json
cahrens committed
5
from lettuce import world, step
6
from nose.tools import assert_equal, assert_true  # pylint: disable=E0611
Julian Arni committed
7
from common import type_in_codemirror, open_new_course
8
from advanced_settings import change_value
Julian Arni committed
9
from course_import import import_file, go_to_import
10
from selenium.webdriver.common.keys import Keys
11

cahrens committed
12
DISPLAY_NAME = "Display Name"
13 14 15 16 17
MAXIMUM_ATTEMPTS = "Maximum Attempts"
PROBLEM_WEIGHT = "Problem Weight"
RANDOMIZATION = 'Randomization'
SHOW_ANSWER = "Show Answer"

cahrens committed
18 19 20

@step('I have created a Blank Common Problem$')
def i_created_blank_common_problem(step):
21
    world.create_course_with_unit()
22 23 24 25 26
    step.given("I have created another Blank Common Problem")


@step('I have created another Blank Common Problem$')
def i_create_new_common_problem(step):
27
    world.create_component_instance(
28 29 30
        step=step,
        category='problem',
        component_type='Blank Common Problem'
31
    )
32

cahrens committed
33 34

@step('I edit and select Settings$')
35
def i_edit_and_select_settings(_step):
cahrens committed
36 37
    world.edit_component_and_select_settings()

38

39 40
@step('I see the advanced settings and their expected values$')
def i_see_advanced_settings_with_values(step):
cahrens committed
41 42
    world.verify_all_setting_entries(
        [
43
            [DISPLAY_NAME, "Blank Common Problem", True],
cahrens committed
44 45
            [MAXIMUM_ATTEMPTS, "", False],
            [PROBLEM_WEIGHT, "", False],
46
            [RANDOMIZATION, "Never", False],
47
            [SHOW_ANSWER, "Finished", False],
cahrens committed
48 49
        ])

50

cahrens committed
51
@step('I can modify the display name')
52
def i_can_modify_the_display_name(_step):
53 54
    # 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).
55
    index = world.get_setting_entry_index(DISPLAY_NAME)
56
    set_field_value(index, '3.4')
cahrens committed
57 58
    verify_modified_display_name()

59

cahrens committed
60 61 62 63 64
@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()

65

66
@step('I can specify special characters in the display name')
67
def i_can_modify_the_display_name_with_special_chars(_step):
68
    index = world.get_setting_entry_index(DISPLAY_NAME)
69
    set_field_value(index, "updated ' \" &")
70 71
    verify_modified_display_name_with_special_chars()

72

73 74 75 76 77
@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()

78

cahrens committed
79
@step('I can revert the display name to unset')
80
def can_revert_display_name_to_unset(_step):
cahrens committed
81 82 83
    world.revert_setting_entry(DISPLAY_NAME)
    verify_unset_display_name()

84

cahrens committed
85 86 87 88 89
@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()

90

cahrens committed
91
@step('I can select Per Student for Randomization')
92
def i_can_select_per_student_for_randomization(_step):
cahrens committed
93 94 95
    world.browser.select(RANDOMIZATION, "Per Student")
    verify_modified_randomization()

96

cahrens committed
97 98 99 100 101
@step('my change to randomization is persisted')
def my_change_to_randomization_is_persisted(step):
    world.save_component_and_reopen(step)
    verify_modified_randomization()

102

cahrens committed
103 104 105 106
@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)
107
    world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Never", False)
108

cahrens committed
109

110
@step('I can set the weight to "(.*)"?')
111
def i_can_set_weight(_step, weight):
112
    set_weight(weight)
cahrens committed
113 114
    verify_modified_weight()

115

cahrens committed
116
@step('my change to weight is persisted')
117
def my_change_to_weight_is_persisted(step):
cahrens committed
118 119 120
    world.save_component_and_reopen(step)
    verify_modified_weight()

121

cahrens committed
122
@step('I can revert to the default value of unset for weight')
123
def i_can_revert_to_default_for_unset_weight(step):
cahrens committed
124 125
    world.revert_setting_entry(PROBLEM_WEIGHT)
    world.save_component_and_reopen(step)
126 127
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)

cahrens committed
128

129 130 131
@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
132
    # We show the clear button immediately on type, hence the "True" here.
133
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True)
cahrens committed
134 135
    world.save_component_and_reopen(step)
    # But no change was actually ever sent to the model, so on reopen, explicitly_set is False
136
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
137

cahrens committed
138

139 140
@step('if I set the max attempts to "(.*)", it will persist as a valid integer$')
def set_the_max_attempts(step, max_attempts_set):
141 142
    # on firefox with selenium, the behaviour is different.
    # eg 2.34 displays as 2.34 and is persisted as 2
143
    index = world.get_setting_entry_index(MAXIMUM_ATTEMPTS)
144
    set_field_value(index, max_attempts_set)
cahrens committed
145
    world.save_component_and_reopen(step)
146 147 148
    value = world.css_value('input.setting-input', index=index)
    assert value != "", "max attempts is blank"
    assert int(value) >= 0
149

cahrens committed
150 151 152

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

155

cahrens committed
156
@step('Edit High Level Source is visible')
157 158
def edit_high_level_source_links_visible(step):
    verify_high_level_source_links(step, True)
cahrens committed
159

160

cahrens committed
161 162 163 164
@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")
165
    step.given("I see the advanced settings and their expected values")
cahrens committed
166

167

168 169 170 171 172 173 174 175 176
@step('I have enabled latex compiler')
def enable_latex_compiler(step):
    url = world.browser.url
    step.given("I select the Advanced Settings")
    change_value(step, 'use_latex_compiler', True)
    world.visit(url)
    world.wait_for_xmodule()


cahrens committed
177 178
@step('I have created a LaTeX Problem')
def create_latex_problem(step):
179
    world.create_course_with_unit()
180
    step.given('I have enabled latex compiler')
181 182 183 184 185 186
    world.create_component_instance(
        step=step,
        category='problem',
        component_type='Problem Written in LaTeX',
        is_advanced=True
    )
cahrens committed
187

188

189
@step('I edit and compile the High Level Source')
190
def edit_latex_source(_step):
191
    open_high_level_source()
192 193 194 195 196
    type_in_codemirror(1, "hi")
    world.css_click('.hls-compile')


@step('my change to the High Level Source is persisted')
197
def high_level_source_persisted(_step):
198
    def verify_text(driver):
Will Daly committed
199 200
        css_sel = '.problem div>span'
        return world.css_text(css_sel) == 'hi'
201

202
    world.wait_for(verify_text, timeout=10)
203 204 205


@step('I view the High Level Source I see my changes')
206
def high_level_source_in_editor(_step):
207
    open_high_level_source()
208
    assert_equal('hi', world.css_value('.source-edit-box'))
209

Julian Arni committed
210

211 212
@step(u'I have an empty course')
def i_have_empty_course(step):
Julian Arni committed
213
    open_new_course()
214

Julian Arni committed
215

216 217
@step(u'I go to the import page')
def i_go_to_import(_step):
Julian Arni committed
218
    go_to_import()
219 220 221 222


@step(u'I import the file "([^"]*)"$')
def i_import_the_file(_step, filename):
Julian Arni committed
223
    import_file(filename)
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238


@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
239
    msg = json.dumps(msg)     # escape quotes
Julian Arni committed
240
    world.css_has_text("h2.title", msg)
241 242 243 244


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

247 248

def verify_high_level_source_links(step, visible):
249
    if visible:
250 251
        assert_true(world.is_css_present('.launch-latex-compiler'),
                    msg="Expected to find the latex button but it is not present.")
252
    else:
253 254 255
        assert_true(world.is_css_not_present('.launch-latex-compiler'),
                    msg="Expected not to find the latex button but it is present.")

cahrens committed
256
    world.cancel_component(step)
257
    if visible:
258 259
        assert_true(world.is_css_present('.upload-button'),
                    msg="Expected to find the upload button but it is not present.")
260
    else:
261 262
        assert_true(world.is_css_not_present('.upload-button'),
                    msg="Expected not to find the upload button but it is present.")
cahrens committed
263

264

cahrens committed
265
def verify_modified_weight():
266 267
    world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "3.5", True)

cahrens committed
268 269

def verify_modified_randomization():
270 271
    world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Per Student", True)

cahrens committed
272 273

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

276

277 278 279
def verify_modified_display_name_with_special_chars():
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, "updated ' \" &", True)

280

cahrens committed
281
def verify_unset_display_name():
282
    world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, 'Blank Advanced Problem', False)
283 284


285 286 287 288 289 290 291 292 293 294 295 296 297 298
def set_field_value(index, value):
    """
    Set the field to the specified value.

    Note: we cannot use css_fill here because the value is not set
    until after you move away from that field.
    Instead we will find the element, set its value, then hit the Tab key
    to get to the next field.
    """
    elem = world.css_find('div.wrapper-comp-setting input.setting-input')[index]
    elem.value = value
    elem.type(Keys.TAB)


299
def set_weight(weight):
300
    index = world.get_setting_entry_index(PROBLEM_WEIGHT)
301
    set_field_value(index, weight)
302 303 304 305 306


def open_high_level_source():
    world.css_click('a.edit-button')
    world.css_click('.launch-latex-compiler > a')