예제 #1
0
    def remote_methods(self, fixture):
        """A RemoteMethod is a SubResource representing a method on the server side which can be invoked via POSTing to an URL."""
        def callable_object():
            return 'value returned from method'

        encoding = 'koi8_r'  # Deliberate
        remote_method = RemoteMethod(
            'amethod', callable_object,
            MethodResult(mime_type='ttext/hhtml', encoding=encoding))

        @stubclass(Widget)
        class WidgetWithRemoteMethod(Widget):
            def __init__(self, view):
                super(WidgetWithRemoteMethod, self).__init__(view)
                view.add_resource(remote_method)

        wsgi_app = fixture.new_wsgi_app(
            view_slots={'main': WidgetWithRemoteMethod.factory()})
        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', {})
        vassert(browser.raw_html == 'value returned from method')
        vassert(browser.last_response.charset == encoding)
        vassert(browser.last_response.content_type == 'ttext/hhtml')
예제 #2
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())

    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'
    }
예제 #3
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)

    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
예제 #4
0
    def non_writable_events_are_dealt_with_like_invalid_input(self, fixture):
        """If a form submits an Event with access rights that prohibit writing, a ValidationException is raised."""
        class ModelObject(object):
            @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(TestPanel, self).__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 = fixture.new_wsgi_app(child_factory=TestPanel.factory())
        browser = Browser(wsgi_app)
        browser.open('/')

        browser.post(fixture.form.event_channel.get_url().path,
                     {'event.an_event?': ''})
        browser.follow_response()
        input_id = browser.get_id_of('//input[@name="event.an_event?"]')
        error_label = browser.get_html_for('//label')
        vassert(error_label ==
                '<label for="%s" class="error">you cannot do this</label>' %
                input_id)
예제 #5
0
    def checked_arguments(self, fixture):
        """A CheckedRemoteMethod checks and marshalls its parameters using Fields."""
        def callable_object(anint=None, astring=None):
            fixture.method_kwargs = {'anint': anint, 'astring': astring}
            return ''

        remote_method = CheckedRemoteMethod('amethod',
                                            callable_object,
                                            MethodResult(),
                                            immutable=fixture.immutable,
                                            anint=IntegerField(),
                                            astring=Field())

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

        if fixture.immutable:
            browser.open(
                '/_amethod_method?anint=5&astring=SupercalifraGilisticexpialidocious'
            )
        else:
            browser.post('/_amethod_method', {
                'anint': '5',
                'astring': 'SupercalifraGilisticexpialidocious'
            })
        vassert(fixture.method_kwargs == {
            'anint': 5,
            'astring': 'SupercalifraGilisticexpialidocious'
        })
예제 #6
0
    def view_preconditions(self, 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(
                    PageLayout(
                        contents_layout=ColumnLayout('main').with_slots()))
                slot_definitions = {'main': Form.factory('the_form')}
                view = self.define_view('/',
                                        title='Hello',
                                        slot_definitions=slot_definitions)
                failing_precondition = ViewPreCondition(
                    lambda: False, exception=SomeException)
                passing_precondition = ViewPreCondition(lambda: True)
                view.add_precondition(passing_precondition)
                view.add_precondition(failing_precondition)

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

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

        browser.post('/_the_form', {}, status=404)
예제 #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(object):
        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(MyForm, self).__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.post(
        '/__myform_method', {
            'event.myform-an_event?': '',
            '_noredirect': '',
            'myform-_reahl_client_concurrency_digest': '',
            'myform-_reahl_database_concurrency_digest': ''
        })
    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']

    browser.open('/')
    expected_html = browser.get_inner_html_for('//form[1]')
    assert json_dict['widgets']['myform'].startswith(expected_html + '<script')
예제 #8
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))

    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'
예제 #9
0
    def widgets_that_change_during_method_processing(self, fixture):
        """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 = fixture.new_wsgi_app(
            child_factory=fixture.WidgetWithRemoteMethod.factory())
        browser = Browser(wsgi_app)

        browser.post('/_amethod_method', {})
        json_response = json.loads(browser.raw_html)
        vassert(json_response == fixture.expected_response)
예제 #10
0
    def exception_handling(self, fixture):
        """The RemoteMethod sends back the six.text_type() of an exception raised for the specified exception class."""
        def fail():
            raise Exception('I failed')

        remote_method = RemoteMethod('amethod', fail,
                                     MethodResult(catch_exception=Exception))

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

        browser.post('/_amethod_method', {})
        vassert(browser.raw_html == 'I failed')
예제 #11
0
    def exception_handling_for_widgets(self, fixture):
        """How exceptions are handled with WidgetResult."""
        def fail():
            raise Exception('exception text')

        remote_method = RemoteMethod('amethod',
                                     fail,
                                     default_result=fixture.method_result)

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

        with expected(Exception):
            browser.post('/_amethod_method', {})
예제 #12
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(object):
        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(MyForm, self).__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.post(
        '/__myform_method', {
            'event.myform-an_event?some_argument=f~nnystuff': '',
            'myform-_reahl_client_concurrency_digest': '',
            'myform-_reahl_database_concurrency_digest': ''
        })
    assert model_object.received_argument == 'f~nnystuff'
예제 #13
0
    def regenerating_method_results(self, fixture):
        """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 = fixture.new_wsgi_app(remote_method=fixture.remote_method)
        browser = Browser(wsgi_app)

        with CallMonitor(fixture.system_control.orm_control.commit) as monitor:
            browser.post('/_amethod_method', {})
        vassert(browser.raw_html == fixture.expected_response)
        vassert(monitor.times_called == 2)
예제 #14
0
    def exception_handling_for_json(self, fixture):
        """How exceptions are handled with JsonResult."""
        def fail():
            raise Exception('exception text')

        remote_method = RemoteMethod('amethod',
                                     fail,
                                     default_result=fixture.method_result)

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

        browser.post('/_amethod_method', {})
        vassert(browser.raw_html == fixture.exception_response)
        vassert(browser.last_response.charset == fixture.expected_charset)
        vassert(browser.last_response.content_type ==
                fixture.expected_content_type)
예제 #15
0
    def different_kinds_of_result(self, fixture):
        """Different kinds of MethodResult can be specified for a method."""
        def callable_object():
            return fixture.value_to_return

        remote_method = RemoteMethod('amethod',
                                     callable_object,
                                     default_result=fixture.method_result)

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

        browser.post('/_amethod_method', {})
        vassert(
            fixture.results_match(fixture.expected_response, browser.raw_html))
        vassert(browser.last_response.charset == fixture.expected_charset)
        vassert(browser.last_response.content_type ==
                fixture.expected_content_type)
예제 #16
0
    def immutable_remote_methods(self, fixture):
        """A RemoteMethod that is immutable is accessible via GET (instead of POST)."""
        def callable_object():
            return 'value returned from method'

        remote_method = RemoteMethod('amethod',
                                     callable_object,
                                     MethodResult(),
                                     immutable=True)

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

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

        # POSTing to the URL, is not supported
        browser.post('/_amethod_method', {}, status=405)
예제 #17
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(object):
        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(TestPanel, self).__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('/')

    browser.post(
        fixture.form.event_channel.get_url().path, {
            'event.some_form-an_event?': '',
            'field_name': 'illigitimate value',
            'some_form-_reahl_client_concurrency_digest': '',
            'some_form-_reahl_database_concurrency_digest': ''
        })
    browser.follow_response()
    assert model_object.field_name == 'Original value'
예제 #18
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)

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

    with expected(Exception):
        browser.post('/_amethod_method', {})
예제 #19
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(WidgetWithRemoteMethod, self).__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)
            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,
        'widgets': {
            'main': '<main><script type="text/javascript"></script>',
            'coactive1': '<coactive1><script type="text/javascript"></script>',
            'coactive2': '<coactive2><script type="text/javascript"></script>'
        }
    }
예제 #20
0
    def arguments_to_remote_methods(self, fixture):
        """A RemoteMethod can get arguments from a query string or submitted form values, depending on the scenario."""
        def callable_object(**kwargs):
            fixture.method_kwargs = kwargs
            return ''

        remote_method = RemoteMethod('amethod',
                                     callable_object,
                                     MethodResult(),
                                     immutable=fixture.immutable)

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

        kwargs_sent = {'a': 'AAA', 'b': 'BBB'}
        if fixture.immutable:
            browser.open('/_amethod_method?a=AAA&b=BBB')
        else:
            browser.post('/_amethod_method', kwargs_sent)
        vassert(fixture.method_kwargs == kwargs_sent)
예제 #21
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)

    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)
예제 #22
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)

    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
예제 #23
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)

    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
예제 #24
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(object):
        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