def test_edit_other(web_fixture, access_domain_fixture, access_ui_fixture): """If you may only edit (not add) an address, then you may only edit the email address, not the name.""" browser = access_ui_fixture.browser address_book = access_domain_fixture.address_book account = access_domain_fixture.account other_address_book = access_domain_fixture.other_address_book other_address_book.allow(account, can_edit_addresses=True, can_add_addresses=True) Address(address_book=other_address_book, email_address='*****@*****.**', name='Friend').save() web_fixture.log_in(browser=browser, system_account=account) browser.open('/') browser.click(XPath.link().with_text('Address book of [email protected]')) browser.click(XPath.button_labelled('Edit')) # Case: may edit name assert browser.is_element_enabled(XPath.input_labelled('Name')) assert browser.is_element_enabled(XPath.input_labelled('Email')) # Case: may not edit name other_address_book.allow(account, can_edit_addresses=True, can_add_addresses=False ) browser.refresh() assert not browser.is_element_enabled(XPath.input_labelled('Name')) assert browser.is_element_enabled(XPath.input_labelled('Email')) browser.type(XPath.input_labelled('Email'), '*****@*****.**') browser.click(XPath.button_labelled('Update')) assert browser.is_element_present(XPath.paragraph().including_text('Friend: [email protected]'))
def test_inputs_cleared_after_domain_exception_resubmit(web_fixture, disclosed_input_fixture): """After a domain exception followed by a successful submit, saved state on the server is cleared, so that newly rendered inputs on the same URL will have defaulted values again.""" # Also see related comment on test_input_values_retained_upon_domain_exception fixture = disclosed_input_fixture wsgi_app = web_fixture.new_wsgi_app(enable_js=True, child_factory=fixture.MyForm.factory()) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser # First get a domain exception fixture.raise_domain_exception_on_submit = True fixture.default_trigger_field_value = False browser.open('/') browser.click(XPath.input_labelled('Trigger field')) browser.type(XPath.input_labelled('Email'), '*****@*****.**') browser.click(XPath.button_labelled('click me')) assert browser.is_element_present(XPath.paragraph().including_text('Exception raised')) # Then successful commit fixture.raise_domain_exception_on_submit = False browser.click(XPath.button_labelled('click me')) # Values are all defaulted like on a first render assert not browser.is_element_present(XPath.paragraph().including_text('Exception raised')) assert not browser.is_selected(XPath.input_labelled('Trigger field')) assert not browser.is_element_present(XPath.paragraph().including_text('Email')) browser.click(XPath.input_labelled('Trigger field')) assert browser.get_value(XPath.input_labelled('Email')) == ''
def test_correct_tab_order_for_responsive_widgets(web_fixture, disclosed_input_trigger_fixture): """When a user TAB's out of an input that then triggers a change, the tab is ignored and focus stays on the original input so that the tab order can be recalculated.""" fixture = disclosed_input_trigger_fixture fixture.trigger_input_type = TextInput fixture.default_trigger_field_value = False wsgi_app = web_fixture.new_wsgi_app(enable_js=True, child_factory=fixture.MyForm.factory()) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser browser.open('/') # Case: a new input appears in next tab order position assert browser.get_value(XPath.input_labelled('Trigger field')) == 'off' browser.press_tab() assert browser.is_focus_on(XPath.input_labelled('Trigger field')) browser.type(XPath.input_labelled('Trigger field'), 'on', trigger_blur=False, wait_for_ajax=False) browser.press_tab() browser.press_tab() assert browser.is_focus_on(XPath.input_labelled('Email')) # Case: an input disappears from the next tab order position browser.type(XPath.input_labelled('Trigger field'), 'off', trigger_blur=False, wait_for_ajax=False) browser.press_tab() browser.press_tab() assert browser.is_focus_on(XPath.button_labelled('click me'))
def test_required_constraint_js(web_fixture, constraint_rendering_fixture): fixture = constraint_rendering_fixture constraint = RequiredConstraint() class MyForm(Form): def __init__(self, view, name): super().__init__(view, name) self.use_layout(FormLayout()) field = fixture.model_object.fields.an_attribute.with_validation_constraint(constraint) self.layout.add_input(TextInput(self, field)) wsgi_app = web_fixture.new_wsgi_app(child_factory=MyForm.factory('myform'), enable_js=True) web_fixture.reahl_server.set_app(wsgi_app) web_fixture.driver_browser.open('/') web_fixture.driver_browser.type(XPath.input_labelled('an attribute'), 'something', trigger_blur=False, wait_for_ajax=False) web_fixture.driver_browser.press_tab() web_fixture.driver_browser.wait_for_element_not_visible(fixture.error_xpath) web_fixture.driver_browser.type(XPath.input_labelled('an attribute'), '') web_fixture.driver_browser.press_keys(web_fixture.driver_browser.Keys.BACK_SPACE, locator=XPath.input_labelled('an attribute')) # To trigger validation on the field web_fixture.driver_browser.wait_for_element_visible(fixture.error_xpath)
def test_input_values_retained_upon_domain_exception(web_fixture, disclosed_input_fixture): """When a domain exception occurs the values typed into an input are retained on the error page.""" # Note: the implementation of this is different for dynamic stuff than for plain old forms. # # Client state is maintained (for a subsequent GET) by posting it to the server in a hidden input; and also saved on an exception. # This state is used at Widget construction stage (before the POST is handled) to ensure all Widgets on the View are built as # before, and as per the client state. # # This test is about how that state is managed and when which version of the state (saved in DB, POSTed etc) takes precedence. fixture = disclosed_input_fixture fixture.raise_domain_exception_on_submit = True fixture.default_trigger_field_value = False wsgi_app = web_fixture.new_wsgi_app(enable_js=True, child_factory=fixture.MyForm.factory()) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser browser.open('/') assert not browser.is_element_present(XPath.input_labelled('Email')) browser.click(XPath.input_labelled('Trigger field')) browser.type(XPath.input_labelled('Email'), '*****@*****.**') browser.click(XPath.button_labelled('click me')) assert browser.is_element_present(XPath.paragraph().including_text('Exception raised')) assert browser.is_selected(XPath.input_labelled('Trigger field')) assert browser.get_value(XPath.input_labelled('Email')) == '*****@*****.**'
def test_input_validation_cues(sql_alchemy_fixture, validation_scenarios): """Visible cues are inserted to indicate the current validation state and possible validation error messages to a user. """ fixture = validation_scenarios browser = fixture.browser with sql_alchemy_fixture.persistent_test_classes(fixture.ModelObject): fixture.domain_object = fixture.ModelObject() Session.add(fixture.domain_object) browser.open('/') assert not fixture.get_form_group_highlight_marks(browser, index=0) assert not fixture.get_form_group_errors(browser, index=0) browser.type(XPath.input_labelled('Some input'), '') browser.click(XPath.button_labelled('Submit')) assert ['is-invalid' ] == fixture.get_form_group_highlight_marks(browser, index=0) [error] = fixture.get_form_group_errors(browser, index=0) assert error.text == 'Some input is required' browser.type(XPath.input_labelled('Some input'), 'valid value') browser.click(XPath.button_labelled('Submit')) assert ['is-valid'] == fixture.get_form_group_highlight_marks(browser, index=0) assert not fixture.get_form_group_errors(browser, index=0) browser.type(XPath.input_labelled('Another input'), 'valid value') browser.click(XPath.button_labelled('Submit')) assert not fixture.get_form_group_highlight_marks(browser, index=0) assert not fixture.get_form_group_errors(browser, index=0)
def test_marshalling_of_radio_button_select_input(web_fixture, input_fixture, field_scenario): """When a form is submitted, the value of the selected radio button is persisted.""" fixture = input_fixture choices = [Choice(1, IntegerField(label='One')), Choice(2, IntegerField(label='Two'))] fixture.field = field_scenario.field_class(choices) model_object = fixture.model_object model_object.an_attribute = field_scenario.default_domain_value wsgi_app = web_fixture.new_wsgi_app(child_factory=fixture.new_Form(input_widget_class=RadioButtonSelectInput).factory('myform')) web_fixture.reahl_server.set_app(wsgi_app) web_fixture.driver_browser.open('/') radio_one = XPath.input_labelled('One') radio_two = XPath.input_labelled('Two') # One is pre selected assert web_fixture.driver_browser.is_selected(radio_one) assert not web_fixture.driver_browser.is_selected(radio_two) # click on Two web_fixture.driver_browser.click(radio_two) assert not web_fixture.driver_browser.is_selected(radio_one) web_fixture.driver_browser.click(XPath.button_labelled('click me')) assert model_object.an_attribute == field_scenario.selected_domain_value
def input_validation_cues(fixture): """Visible cues are inserted to indicate the current validation state and possible validation error messages to a user. """ browser = fixture.browser browser.open('/') vassert(not fixture.get_form_group_highlight_marks(browser, index=0)) vassert(not fixture.get_form_group_errors(browser, index=0)) browser.type(XPath.input_labelled('Some input'), '') browser.click(XPath.button_labelled('Submit')) vassert(['has-danger'] == fixture.get_form_group_highlight_marks(browser, index=0)) [error] = fixture.get_form_group_errors(browser, index=0) vassert(error.text == 'Some input is required') browser.type(XPath.input_labelled('Some input'), 'valid value') browser.click(XPath.button_labelled('Submit')) vassert(['has-success'] == fixture.get_form_group_highlight_marks(browser, index=0)) vassert(not fixture.get_form_group_errors(browser, index=0)) browser.type(XPath.input_labelled('Another input'), 'valid value') browser.click(XPath.button_labelled('Submit')) vassert(not fixture.get_form_group_highlight_marks(browser, index=0)) vassert(not fixture.get_form_group_errors(browser, index=0))
def test_async_number_files_validation(web_fixture, max_number_of_files_file_upload_input_fixture): """A Field set to only allow a maximum number of files is checked for validity before uploading in JS. """ # Only tested for the FileUploadInput, as it uses the FileInput # in its own implementation, in a NestedForm, and has to pass on the # filesize constraint all the way. This way, we test all of that. fixture = max_number_of_files_file_upload_input_fixture web_fixture.reahl_server.set_app(fixture.new_wsgi_app(enable_js=True)) browser = web_fixture.driver_browser browser.open('/') assert not fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload1.name) assert fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) # Corner case: max are uploaded, but you've not asked to add to them yet: assert browser.wait_for_not(browser.is_visible, XPath.span().including_text('a maximum of 1 files may be uploaded')) # Normal case: max are uploaded, and you're asking to upload another: browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload2.name) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) assert browser.wait_for(browser.is_visible, XPath.span().including_text('a maximum of 1 files may be uploaded')) browser.click(XPath.button_labelled('Remove', filename=fixture.file_to_upload1_name)) assert browser.wait_for_not(browser.is_visible, XPath.span().including_text('a maximum of 1 files may be uploaded'))
def test_input_validation_cues_javascript_interaction(web_fixture, sql_alchemy_fixture, javascript_validation_scenario): """The visual cues rendered server-side can subsequently be manipulated via javascript.""" fixture = javascript_validation_scenario web_fixture.reahl_server.set_app(web_fixture.new_wsgi_app(child_factory=fixture.Form.factory(), enable_js=False)) browser = fixture.browser with sql_alchemy_fixture.persistent_test_classes(fixture.ModelObject): fixture.domain_object = fixture.ModelObject() Session.add(fixture.domain_object) browser.open('/') browser.type(XPath.input_labelled('Some input'), '') browser.click(XPath.button_labelled('Submit')) assert ['is-invalid'] == fixture.get_form_group_highlight_marks(browser, index=0) [error] = fixture.get_form_group_errors(browser, index=0) assert error.text == 'Some input is required' web_fixture.reahl_server.set_app(web_fixture.new_wsgi_app(child_factory=fixture.Form.factory(), enable_js=True)) browser.open('/') browser.click(XPath.button_labelled('Submit')) assert ['is-invalid'] == fixture.get_form_group_highlight_marks(browser, index=0) [error] = fixture.get_form_group_errors(browser, index=0) assert error.text == 'Some input is required' browser.type(XPath.input_labelled('Some input'), 'valid value', trigger_blur=False, wait_for_ajax=False) browser.press_tab() def form_group_is_marked_success(index): return ['is-valid'] == fixture.get_form_group_highlight_marks(browser, index=index) assert web_fixture.driver_browser.wait_for(form_group_is_marked_success, 0) assert not fixture.get_form_group_errors(browser, index=0)
def test_async_upload_domain_exception(web_fixture, toggle_validation_fixture): """When a DomainException happens upon uploading via JavaScript, the form is replaced with a rerendered version from the server.""" fixture = toggle_validation_fixture web_fixture.reahl_server.set_app(fixture.new_wsgi_app(enable_js=True)) browser = web_fixture.driver_browser browser.open('/') fixture.make_validation_fail = False browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload1.name) fixture.make_validation_fail = True fixture.mark_nested_form() with browser.no_page_load_expected(): browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload2.name, trigger_blur=False) assert fixture.nested_form_was_reloaded() # JS Stuff on re-rendered form still work # 1: Server-rendered validation message has been cleared assert browser.is_visible(XPath.span().including_text('test validation message')) fixture.make_validation_fail = False with browser.no_page_load_expected(): browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload2.name) browser.wait_for_not(browser.is_visible, XPath.span().including_text('test validation message')) # 2: The remove button still happens via ajax with browser.no_page_load_expected(): browser.click(XPath.button_labelled('Remove', filename=fixture.file_to_upload1_name)) browser.click(XPath.button_labelled('Remove', filename=fixture.file_to_upload2_name))
def test_async_remove(web_fixture, file_upload_input_fixture): """With javascript enabled, removing of uploaded files take place via ajax.""" fixture = file_upload_input_fixture web_fixture.reahl_server.set_app(fixture.new_wsgi_app(enable_js=True)) browser = web_fixture.driver_browser browser.open('/') # Upload two files browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload1.name) browser.click(XPath.button_labelled('Upload')) browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload2.name) browser.click(XPath.button_labelled('Upload')) # Remove file1 with browser.no_page_load_expected(): browser.click(XPath.button_labelled('Remove', filename=fixture.file_to_upload1_name)) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) assert fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) # The javascript works on DOM elements that have been generated server-side as well: browser.refresh() with browser.no_page_load_expected(): browser.click(XPath.button_labelled('Remove', filename=fixture.file_to_upload2_name)) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) # No files are submitted eventually browser.click( XPath.button_labelled('Submit') ) assert list(fixture.domain_object.submitted_file_info.keys()) == []
def test_cancelling_queued_upload(web_fixture, large_file_upload_input_fixture): """Cancelling an upload that is still queued (upload not started yet) removes the file from the list and removed it from the queue of uploads. """ fixture = large_file_upload_input_fixture fixture.run_hook_before = True web_fixture.reahl_server.set_app(fixture.new_wsgi_app(enable_js=True)) browser = web_fixture.driver_browser browser.open('/') assert not fixture.file_was_uploaded( fixture.file_to_upload1.name ) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) assert not fixture.file_was_uploaded( fixture.file_to_upload2.name ) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) with web_fixture.reahl_server.in_background(wait_till_done_serving=False): browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload1.name, wait_for_ajax=False) # Upload will block, see fixture browser.type(XPath.input_labelled('Choose file(s)'), fixture.file_to_upload2.name, wait_for_ajax=False) # Upload will block, see fixture browser.wait_for(fixture.upload_file_is_queued, fixture.file_to_upload1.name) browser.wait_for(fixture.upload_file_is_queued, fixture.file_to_upload2.name) browser.click(XPath.button_labelled('Cancel', filename=fixture.file_to_upload2_name), wait_for_ajax=False) browser.wait_for_not(fixture.upload_file_is_queued, fixture.file_to_upload2.name) fixture.simulate_large_file_upload_done() browser.wait_for(fixture.uploaded_file_is_listed, fixture.file_to_upload1.name) assert fixture.uploaded_file_is_listed( fixture.file_to_upload1.name ) assert fixture.file_was_uploaded( fixture.file_to_upload1.name ) assert not fixture.uploaded_file_is_listed( fixture.file_to_upload2.name ) assert not fixture.file_was_uploaded( fixture.file_to_upload2.name )
def test_verify_from_menu(web_fixture, party_account_fixture, accounts_web_fixture): fixture = accounts_web_fixture account = party_account_fixture.new_system_account(activated=False) activation_request = VerifyEmailRequest( email=account.email, subject_config='accounts.activation_subject', email_config='accounts.activation_email') Session.add(activation_request) deferred_activation = ActivateAccount(system_account=account, requirements=[activation_request]) Session.add(deferred_activation) secret_key = activation_request.as_secret_key() assert not account.status.is_active() fixture.browser.open('/a_ui/verify') fixture.browser.type(XPath.input_labelled('Email'), account.email) fixture.browser.type(XPath.input_labelled('Secret key'), secret_key) fixture.browser.type(XPath.input_labelled('Password'), account.password) fixture.browser.click(XPath.button_labelled('Verify')) assert fixture.browser.current_url.path == '/a_ui/thanks' assert account.status.is_active()
def cue_input_display_basics(fixture): """A CueInput displays a given cue when its wrapped Input has focus and hides the cue otherwise.""" class FormWithCueInput(Form): def __init__(self, view): super(FormWithCueInput, self).__init__(view, 'test') self.use_layout(FormLayout()) cue_input = CueInput( TextInput(self, fixture.domain_object.fields.field), P(view, 'this is your cue')) self.layout.add_input(cue_input) wsgi_app = fixture.new_wsgi_app(child_factory=FormWithCueInput.factory(), enable_js=True) fixture.reahl_server.set_app(wsgi_app) browser = fixture.driver_browser browser.open('/') #initially the cue is not visible browser.wait_for_element_not_visible(fixture.cue_element_xpath) #tabbing to the input reveals the cue browser.focus_on(XPath.input_labelled('MyField')) browser.wait_for_element_visible(fixture.cue_element_xpath) #moving focus to another input causes the cue to be hidden browser.press_tab(XPath.input_labelled('MyField')) browser.wait_for_element_not_visible(fixture.cue_element_xpath)
def test_marshalling_of_checkbox_select_input(web_fixture, checkbox_fixture): """CheckboxSelectInput is used to choose many things from a list.""" fixture = checkbox_fixture choices = [Choice(1, IntegerField(label='One')), Choice(2, IntegerField(label='Two')), Choice(3, IntegerField(label='Three'))] fixture.field = MultiChoiceField(choices) model_object = fixture.model_object model_object.an_attribute = [1] wsgi_app = web_fixture.new_wsgi_app(child_factory=fixture.new_Form(input_widget_class=CheckboxSelectInput).factory('myform')) web_fixture.reahl_server.set_app(wsgi_app) web_fixture.driver_browser.open('/') assert web_fixture.driver_browser.is_selected(XPath.input_labelled('One')) assert not web_fixture.driver_browser.is_selected(XPath.input_labelled('Two')) assert not web_fixture.driver_browser.is_selected(XPath.input_labelled('Three')) # Case: checkbox is submitted with form (ie checked) web_fixture.driver_browser.set_deselected(XPath.input_labelled('One')) web_fixture.driver_browser.set_selected(XPath.input_labelled('Two')) web_fixture.driver_browser.set_selected(XPath.input_labelled('Three')) web_fixture.driver_browser.click(XPath.button_labelled('click me')) assert model_object.an_attribute == [2, 3] assert fixture.checkbox.value == '2,3' assert not web_fixture.driver_browser.is_selected(XPath.input_labelled('One')) assert web_fixture.driver_browser.is_selected(XPath.input_labelled('Two')) assert web_fixture.driver_browser.is_selected(XPath.input_labelled('Three'))