Example #1
0
def test_non_writable_events_are_dealt_with_like_invalid_input(web_fixture):
    """If a form submits an Event with access rights that prohibit writing, a ValidationException is raised."""
    fixture = web_fixture

    class ModelObject:
        @exposed
        def events(self, events):
            events.an_event = Event(label='click me', writable=Allowed(False),
                                    disallowed_message='you cannot do this')

    model_object = ModelObject()
    class TestPanel(Div):
        def __init__(self, view):
            super().__init__(view)
            form = self.add_child(Form(view, 'some_form'))
            form.define_event_handler(model_object.events.an_event)
            button = form.add_child(ButtonInput(form, model_object.events.an_event))
            if button.validation_error:
                form.add_child(form.create_error_label(button))
            fixture.form = form


    wsgi_app = web_fixture.new_wsgi_app(child_factory=TestPanel.factory())
    browser = Browser(wsgi_app)
    browser.open('/')

    csrf_token = browser.get_value('//input[@name="some_form-_reahl_csrf_token"]')
    browser.post(fixture.form.event_channel.get_url().path, {'event.some_form-an_event?':'', 'some_form-_reahl_database_concurrency_digest':'', 'some_form-_reahl_csrf_token': csrf_token})
    browser.follow_response()
    error_label = browser.get_html_for('//label')
    input_id = browser.get_id_of('//input[@name="event.some_form-an_event?"]')
    assert error_label == '<label for="%s" class="error">you cannot do this</label>' % input_id
Example #2
0
def test_query_string_prepopulates_form(web_fixture, value_scenarios):
    """Widget query string arguments can be used on forms to pre-populate inputs based on the query string."""

    fixture = value_scenarios

    class ModelObject:
        @exposed
        def fields(self, fields):
            fields.arg_on_other_object = fixture.field

    class FormWithQueryArguments(Form):
        def __init__(self, view):
            self.model_object = ModelObject()
            super().__init__(view, 'name')
            self.use_layout(FormLayout())
            self.layout.add_input(
                TextInput(self, self.model_object.fields.arg_on_other_object))

        @exposed
        def query_fields(self, fields):
            fields.arg_on_other_object = self.model_object.fields.arg_on_other_object

    wsgi_app = web_fixture.new_wsgi_app(
        enable_js=True, child_factory=FormWithQueryArguments.factory())
    browser = Browser(wsgi_app)

    browser.open('/?%s' % fixture.field_on_query_string.format(
        field_name='name-arg_on_other_object'))
    assert browser.get_value(
        XPath.input_labelled('field')) == fixture.field_value_as_string
Example #3
0
def test_non_writable_input_is_dealt_with_like_invalid_input(web_fixture):
    """If a form submits a value for an Input that is linked to Field with access rights that prohibit writing,
       the input is silently ignored."""
    fixture = web_fixture

    class ModelObject:
        field_name = 'Original value'
        @exposed
        def events(self, events):
            events.an_event = Event(label='click me')

        @exposed
        def fields(self, fields):
            fields.field_name = Field(default='abc', writable=Allowed(False), disallowed_message='you are not allowed to write this')
    model_object = ModelObject()

    class TestPanel(Div):
        def __init__(self, view):
            super().__init__(view)
            form = self.add_child(Form(view, 'some_form'))
            form.define_event_handler(model_object.events.an_event)
            form.add_child(ButtonInput(form, model_object.events.an_event))
            form.add_child(TextInput(form, model_object.fields.field_name))

            fixture.form = form


    wsgi_app = web_fixture.new_wsgi_app(child_factory=TestPanel.factory())
    browser = Browser(wsgi_app)
    browser.open('/')

    csrf_token = browser.get_value('//input[@name="some_form-_reahl_csrf_token"]')
    browser.post(fixture.form.event_channel.get_url().path, {'event.some_form-an_event?':'', 'some_form-field_name': 'illigitimate value', 'some_form-_reahl_database_concurrency_digest':'', 'some_form-_reahl_csrf_token': csrf_token})
    browser.follow_response()
    assert model_object.field_name == 'Original value'
Example #4
0
def test_alternative_event_trigerring(web_fixture):
    """Events can also be triggered by submitting a Form via Ajax. In such cases the normal redirect-after-submit
       behaviour of the underlying EventChannel is not desirable. This behaviour can be switched off by submitting
       an extra argument along with the Form in order to request the alternative behaviour.
    """

    fixture = web_fixture

    class ModelObject:
        def handle_event(self):
            self.handled_event = True

        @exposed
        def events(self, events):
            events.an_event = Event(label='click me',
                                    action=Action(self.handle_event))

    model_object = ModelObject()

    class MyForm(Form):
        def __init__(self, view, name, other_view):
            super().__init__(view, name)
            self.define_event_handler(model_object.events.an_event,
                                      target=other_view)
            self.add_child(ButtonInput(self, model_object.events.an_event))

    class MainUI(UserInterface):
        def assemble(self):
            self.define_page(HTML5Page).use_layout(BasicPageLayout())
            home = self.define_view('/', title='Home page')
            other_view = self.define_view('/page2', title='Page 2')
            home.set_slot('main', MyForm.factory('myform', other_view))

    wsgi_app = fixture.new_wsgi_app(site_root=MainUI)
    browser = Browser(wsgi_app)

    # when POSTing with _noredirect, the Action is executed, but the browser is not redirected to /page2 as usual
    browser.open('/')
    csrf_token = browser.get_value('//input[@name="myform-_reahl_csrf_token"]')
    browser.post(
        '/__myform_method', {
            'event.myform-an_event?': '',
            '_noredirect': '',
            'myform-_reahl_database_concurrency_digest': '',
            'myform-_reahl_csrf_token': csrf_token
        })
    browser.follow_response(
    )  # Needed to make the test break should a HTTPTemporaryRedirect response be sent
    assert model_object.handled_event
    assert browser.current_url.path != '/page2'
    assert browser.current_url.path == '/__myform_method'

    # the response is a json object reporting the success of the event and a new rendition of the form
    json_dict = json.loads(browser.raw_html)
    assert json_dict['success']
    expected_html = '<div id="myform_hashes">'
    assert json_dict['result']['myform'].startswith(expected_html)
Example #5
0
def test_event_names_are_canonicalised(web_fixture):
    """The name= attribute of a button is an url encoded string. There is more than one way
       to url encode the same string. The server ensures that different encodings of the same
       string are not mistaken for different names.
    """

    fixture = web_fixture

    class ModelObject:
        def handle_event(self, some_argument):
            self.received_argument = some_argument

        @exposed
        def events(self, events):
            events.an_event = Event(
                label='click me',
                action=Action(self.handle_event, ['some_argument']),
                some_argument=Field(default='default value'))

    model_object = ModelObject()

    class MyForm(Form):
        def __init__(self, view, name):
            super().__init__(view, name)
            self.define_event_handler(model_object.events.an_event)
            self.add_child(
                ButtonInput(
                    self,
                    model_object.events.an_event.with_arguments(
                        some_argument='f~nnystuff')))

    class MainUI(UserInterface):
        def assemble(self):
            self.define_page(HTML5Page).use_layout(BasicPageLayout())
            home = self.define_view('/', title='Home page')
            home.set_slot('main', MyForm.factory('myform'))

    wsgi_app = fixture.new_wsgi_app(site_root=MainUI)
    browser = Browser(wsgi_app)

    # when the Action is executed, the correct arguments are passed
    browser.open('/')
    csrf_token = browser.get_value('//input[@name="myform-_reahl_csrf_token"]')
    browser.post(
        '/__myform_method', {
            'event.myform-an_event?some_argument=f~nnystuff': '',
            'myform-_reahl_database_concurrency_digest': '',
            'myform-_reahl_csrf_token': csrf_token
        })
    assert model_object.received_argument == 'f~nnystuff'
Example #6
0
def test_email_retained(web_fixture, session_scope_fixture):
    """The email address used when last logged in is always pre-populated on the Log in page."""

    browser = Browser(web_fixture.new_wsgi_app(site_root=SessionScopeUI))
    user = session_scope_fixture.user

    browser.open('/')
    browser.click(XPath.link().with_text('Log in'))

    browser.type(XPath.input_labelled('Email'), '*****@*****.**')
    browser.type(XPath.input_labelled('Password'), 'topsecret')
    browser.click(XPath.button_labelled('Log in'))

    # Go away from the page, then back
    browser.click(XPath.link().with_text('Home'))
    browser.click(XPath.link().with_text('Log in'))

    # .. then the email is still pre-populated
    typed_value = browser.get_value(XPath.input_labelled('Email'))
    assert typed_value == '*****@*****.**'
Example #7
0
def test_form_preserves_user_input_after_validation_exceptions_multichoice(
        web_fixture):
    """When a form is submitted and validation fails on the server, the user
     is presented with the values that were originally entered (even if they were invalid)."""

    fixture = web_fixture

    class ModelObject:
        @exposed
        def events(self, events):
            events.an_event = Event(label='click me')

        @exposed
        def fields(self, fields):
            choices = [
                Choice(1, IntegerField(label='One')),
                Choice(2, IntegerField(label='Two')),
                Choice(3, IntegerField(label='Three'))
            ]
            fields.no_validation_exception_field = MultiChoiceField(
                choices, label='Make your invalid choice', default=[])
            fields.validation_exception_field = MultiChoiceField(
                choices, label='Make your choice', default=[], required=True)

    model_object = ModelObject()

    class MyForm(Form):
        def __init__(self, view):
            super().__init__(view, 'my_form')
            self.define_event_handler(model_object.events.an_event)
            self.add_child(ButtonInput(self, model_object.events.an_event))
            select_input = self.add_child(
                SelectInput(self,
                            model_object.fields.no_validation_exception_field))
            if select_input.validation_error:
                self.add_child(self.create_error_label(select_input))
            select_input = self.add_child(
                SelectInput(self,
                            model_object.fields.validation_exception_field))
            if select_input.validation_error:
                self.add_child(self.create_error_label(select_input))

    wsgi_app = web_fixture.new_wsgi_app(child_factory=MyForm.factory())
    browser = Browser(wsgi_app)

    browser.open('/')

    no_validation_exception_input = '//select[@name="my_form-no_validation_exception_field[]"]'
    validation_exception_input = '//select[@name="my_form-validation_exception_field[]"]'

    browser.select_many(no_validation_exception_input, ['One', 'Two'])
    browser.select_none(validation_exception_input
                        )  # select none to trigger the RequiredConstraint
    browser.click(XPath.button_labelled('click me'))

    assert browser.get_value(no_validation_exception_input) == ['1', '2']
    assert not browser.get_value(validation_exception_input)

    label = browser.get_html_for('//label')
    input_id = browser.get_id_of(validation_exception_input)
    assert label == '<label for="%s" class="error">Make your choice is required</label>' % input_id

    #2. Submit again ths time not expecting validation exceptions, also expecting the validation error to be cleared and the domain should have all input
    browser.select_many(validation_exception_input, ['Two', 'Three'])
    browser.click(XPath.button_labelled('click me'))

    assert not browser.is_element_present('//label[@class="error"]')
    assert browser.get_value(no_validation_exception_input) == ['1', '2']
    assert browser.get_value(validation_exception_input) == ['2', '3']
Example #8
0
def test_exception_handling(reahl_system_fixture, web_fixture,
                            sql_alchemy_fixture):
    """When a DomainException happens during the handling of an Event:

       The database is rolled back.
       The browser is redirected to GET the original view again (not the target).
       The screen still displays the values the user initially typed, not those on the ModelObject.
    """

    fixture = web_fixture

    class ModelObject(Base):
        __tablename__ = 'test_event_handling_exception_handling'
        id = Column(Integer, primary_key=True)
        field_name = Column(Integer)

        def handle_event(self):
            self.field_name = 1
            raise DomainException()

        @exposed
        def events(self, events):
            events.an_event = Event(label='click me',
                                    action=Action(self.handle_event))

        @exposed
        def fields(self, fields):
            fields.field_name = IntegerField(default=3)

    with sql_alchemy_fixture.persistent_test_classes(ModelObject):
        model_object = ModelObject()
        Session.add(model_object)

        class MyForm(Form):
            def __init__(self, view, name, other_view):
                super().__init__(view, name)
                self.define_event_handler(model_object.events.an_event,
                                          target=other_view)
                self.add_child(ButtonInput(self, model_object.events.an_event))
                self.add_child(TextInput(self, model_object.fields.field_name))

        class MainUI(UserInterface):
            def assemble(self):
                self.define_page(HTML5Page).use_layout(BasicPageLayout())
                home = self.define_view('/', title='Home page')
                other_view = self.define_view('/page2', title='Page 2')
                home.set_slot('main', MyForm.factory('myform', other_view))

        wsgi_app = fixture.new_wsgi_app(site_root=MainUI)

        browser = Browser(
            wsgi_app
        )  # Dont use a real browser, because it will also hit many other URLs for js and css which confuse the issue
        browser.open('/')

        assert not model_object.field_name
        browser.type("//input[@type='text']", '5')

        # any database stuff that happened when the form was submitted was rolled back
        #        with CallMonitor(reahl_system_fixture.system_control.orm_control.rollback) as monitor:
        #            browser.click(XPath.button_labelled('click me'))
        #        assert monitor.times_called == 1
        browser.click(XPath.button_labelled('click me'))

        # the value input by the user is still displayed on the form, NOT the actual value on the model object
        assert not model_object.field_name
        retained_value = browser.get_value("//input[@type='text']")
        assert retained_value == '5'

        # the browser is still on the page with the form which triggered the exception
        assert browser.current_url.path == '/'