示例#1
0
def test_remote_methods(web_fixture, remote_method_fixture):
    """A RemoteMethod is a SubResource representing a method on the server side which can be invoked via POSTing to an URL."""

    fixture = remote_method_fixture

    def callable_object():
        return 'value returned from method'

    encoding = 'koi8_r'  # Deliberate
    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 callable_object,
                                 MethodResult(mime_type='ttext/hhtml',
                                              encoding=encoding),
                                 disable_csrf_check=True)

    wsgi_app = fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    # By default you cannot GET, since the method is not immutable
    browser.open('/_amethod_method', status=405)

    # POSTing to the URL, returns the result of the method
    browser.post('/_amethod_method', {})
    assert browser.raw_html == 'value returned from method'
    assert browser.last_response.charset == encoding
    assert browser.last_response.content_type == 'ttext/hhtml'
示例#2
0
def test_arguments_to_remote_methods(web_fixture, remote_method_fixture,
                                     argument_scenarios):
    """A RemoteMethod can get arguments from a query string or submitted form values, depending on the scenario."""

    fixture = argument_scenarios

    def callable_object(**kwargs):
        fixture.method_kwargs = kwargs
        return ''

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 callable_object,
                                 MethodResult(),
                                 idempotent=fixture.idempotent,
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    kwargs_sent = {'a': 'AAA', 'b': 'BBB'}
    if fixture.idempotent:
        browser.open('/_amethod_method?a=AAA&b=BBB')
    else:
        browser.post('/_amethod_method', kwargs_sent)
    assert fixture.method_kwargs == kwargs_sent
示例#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'
示例#4
0
def test_checked_arguments(web_fixture, remote_method_fixture,
                           argument_scenarios):
    """A CheckedRemoteMethod checks and marshalls its parameters using Fields."""

    fixture = argument_scenarios

    def callable_object(anint=None, astring=None):
        fixture.method_kwargs = {'anint': anint, 'astring': astring}
        return ''

    remote_method = CheckedRemoteMethod(web_fixture.view,
                                        'amethod',
                                        callable_object,
                                        MethodResult(),
                                        idempotent=fixture.idempotent,
                                        anint=IntegerField(),
                                        astring=Field(),
                                        disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    if fixture.idempotent:
        browser.open(
            '/_amethod_method?anint=5&astring=SupercalifraGilisticexpialidocious'
        )
    else:
        browser.post('/_amethod_method', {
            'anint': '5',
            'astring': 'SupercalifraGilisticexpialidocious'
        })
    assert fixture.method_kwargs == {
        'anint': 5,
        'astring': 'SupercalifraGilisticexpialidocious'
    }
示例#5
0
def test_remote_methods_via_ajax(web_fixture, remote_method_fixture):
    """A RemoteMethod can be called via AJAX with CSRF protection built-in."""

    fixture = remote_method_fixture

    def callable_object():
        return 'value returned from method'

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 callable_object,
                                 MethodResult(),
                                 disable_csrf_check=False)

    wsgi_app = fixture.new_wsgi_app(remote_method=remote_method,
                                    enable_js=True)
    web_fixture.reahl_server.set_app(wsgi_app)
    browser = web_fixture.driver_browser

    # Case: using jquery to POST to the method includes the necessary xsrf info automatically
    browser.open('/')
    browser.execute_script(
        '$.post("/_amethod_method", success=function(data){ $("body").attr("data-result", data) })'
    )
    results = browser.execute_script('return $("body").attr("data-result")')
    assert results == 'value returned from method'

    # Case: POSTing without a csrf token breaks
    browser = Browser(wsgi_app)
    browser.post('/_amethod_method', {}, status=403)
示例#6
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
示例#7
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)
示例#8
0
def test_widgets_that_change_during_method_processing(web_fixture,
                                                      widget_result_scenarios):
    """The Widget rendered by WidgetResult reflects its Widget as it would have
       looked if it were constructed AFTER the changes effected by executing
       its RemoteMethod have been committed.
    """

    wsgi_app = web_fixture.new_wsgi_app(
        child_factory=widget_result_scenarios.WidgetWithRemoteMethod.factory())
    browser = Browser(wsgi_app)

    browser.post('/_amethod_method', {})
    json_response = json.loads(browser.raw_html)
    assert json_response == widget_result_scenarios.expected_response
示例#9
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'
示例#10
0
def test_exception_handling(web_fixture, remote_method_fixture):
    """The RemoteMethod sends back the str() of an exception raised for the specified exception class."""
    def fail():
        raise Exception('I failed')

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 fail,
                                 MethodResult(catch_exception=Exception),
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    browser.post('/_amethod_method', {})
    assert browser.raw_html == 'I failed'
示例#11
0
def test_coactive_widgets(web_fixture):
    """Coactive Widgets of a Widget are Widgets that are included in a WidgetResult for that Widget.

    Included are: the coactive widgets of children of the result widget as well as the coactive widgets of coactive widgets.
    """
    @stubclass(Widget)
    class WidgetWithRemoteMethod(Widget):
        def __init__(self, view):
            super().__init__(view)
            coactive_widgets = [
                self.add_child(
                    CoactiveWidgetStub(view, 'coactive1', [
                        self.add_child(
                            CoactiveWidgetStub(view, 'coactive2', []))
                    ]))
            ]
            result_widget = self.add_child(CoactiveWidgetStub(
                view, 'main', []))
            result_widget.add_child(
                CoactiveWidgetStub(view, 'child', coactive_widgets))
            method_result = WidgetResult(result_widget,
                                         as_json_and_result=True)
            remote_method = RemoteMethod(view,
                                         'amethod',
                                         lambda: None,
                                         default_result=method_result,
                                         disable_csrf_check=True)
            view.add_resource(remote_method)

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

    browser.post('/_amethod_method', {})
    json_response = json.loads(browser.raw_html)
    assert json_response == {
        'success': True,
        'exception': '',
        'result': {
            'main': '<main><script type="text/javascript"></script>',
            'coactive1': '<coactive1><script type="text/javascript"></script>',
            'coactive2': '<coactive2><script type="text/javascript"></script>'
        }
    }
示例#12
0
def test_exception_handling_for_widgets(web_fixture, remote_method_fixture,
                                        widget_result_scenario):
    """How exceptions are handled with WidgetResult."""

    fixture = widget_result_scenario

    def fail():
        raise Exception('exception text')

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 fail,
                                 default_result=fixture.method_result,
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    with expected(Exception):
        browser.post('/_amethod_method', {})
示例#13
0
def test_idempotent_remote_methods(web_fixture, remote_method_fixture):
    """A RemoteMethod that is idempotent is accessible via GET (instead of POST)."""
    def callable_object():
        return 'value returned from method'

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 callable_object,
                                 MethodResult(),
                                 idempotent=True,
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    # GET, since the method is idempotent
    browser.open('/_amethod_method')
    assert browser.raw_html == 'value returned from method'

    # POSTing to the URL, is not supported
    browser.post('/_amethod_method', {}, status=405)
示例#14
0
def test_different_kinds_of_result(web_fixture, remote_method_fixture,
                                   result_scenarios):
    """Different kinds of MethodResult can be specified for a method."""

    fixture = result_scenarios

    def callable_object():
        return fixture.value_to_return

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 callable_object,
                                 default_result=fixture.method_result,
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    browser.post('/_amethod_method', {})
    assert fixture.results_match(fixture.expected_response, browser.raw_html)
    assert browser.last_response.charset == fixture.expected_charset
    assert browser.last_response.content_type == fixture.expected_content_type
示例#15
0
def test_exception_handling_for_json(web_fixture, remote_method_fixture,
                                     json_result_scenario):
    """How exceptions are handled with JsonResult."""

    fixture = json_result_scenario

    def fail():
        raise Exception('exception text')

    remote_method = RemoteMethod(web_fixture.view,
                                 'amethod',
                                 fail,
                                 default_result=fixture.method_result,
                                 disable_csrf_check=True)

    wsgi_app = remote_method_fixture.new_wsgi_app(remote_method=remote_method)
    browser = Browser(wsgi_app)

    browser.post('/_amethod_method', {})
    assert browser.raw_html == fixture.exception_response
    assert browser.last_response.charset == fixture.expected_charset
    assert browser.last_response.content_type == fixture.expected_content_type
示例#16
0
def test_view_preconditions(web_fixture):
    """Views can have Conditions attached to them - if a ViewPreCondition fails upon a GET or HEAD request,
       the specified exception is raised. For all other requests, HTTPNotFound is raised."""
    class SomeException(Exception):
        pass

    class MainUI(UserInterface):
        def assemble(self):
            self.define_page(HTML5Page).use_layout(BasicPageLayout())
            view = self.define_view('/', title='Hello')
            view.set_slot('main', Form.factory('the_form'))
            failing_precondition = ViewPreCondition(lambda: False, exception=SomeException)
            passing_precondition = ViewPreCondition(lambda: True)
            view.add_precondition(passing_precondition)
            view.add_precondition(failing_precondition)

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

    with expected(SomeException):
        browser.open('/')

    browser.post('/_the_form', {}, status=404)
示例#17
0
def test_regenerating_method_results(reahl_system_fixture, web_fixture,
                                     remote_method_fixture,
                                     regenerate_method_result_scenarios):
    """If a MethodResult is set up to replay_request=True, the view it is part of (and thus itself) is recreated
       before the (new incarnation of the) MethodResult generates its actual response. Replaying the request means recreating
       all Widgets on the current View as well as the MethodResult itself. The construction of any of these
       objects may happen differently because of the changes made during the RemoteMethod's execution. Replaying
       the request ensures that the MethodResult reflects such changes, yet ensures that the RemoteMethod
       is not executed twice.
    """

    wsgi_app = remote_method_fixture.new_wsgi_app(
        remote_method=regenerate_method_result_scenarios.remote_method)
    browser = Browser(wsgi_app)

    import sqlalchemy.orm

    @stubclass(sqlalchemy.orm.Session)
    class TransactionStub:
        is_active = True

        def commit(self):
            pass

        def rollback(self):
            pass

    def wrapped_nested_transaction():
        return web_fixture.nested_transaction

    web_fixture.nested_transaction = TransactionStub()
    with replaced(Session().begin_nested, wrapped_nested_transaction):
        with CallMonitor(web_fixture.nested_transaction.commit) as monitor:
            browser.post('/_amethod_method', {})
        assert browser.raw_html == regenerate_method_result_scenarios.expected_response
        assert monitor.times_called == 2