Form submissions

Testing form submissions

Preface: You want to test your form submission works in success and error situations.

Example form handler view:

"""Subscribe to newsletter."""
import logging

import deform
import deform.widget
import colander as c

from pyramid.httpexceptions import HTTPTooManyRequests
from websauna.system.form.schema import CSRFSchema
from websauna.system.core.route import simple_route

from websauna.system.core import messages
from websauna.system.mail import send_templated_mail
from websauna.system.model import now
from websauna.system.form import rollingwindow

from trees.models import NewsletterSubscriber

#: Which session flag tells that the visitor already went through newsletter dialog
SESSION_SUBSCRIBE_KEY = "subscribed_newsletter"

logger = logging.getLogger(__name__)

class SubscribeNewsLetter(CSRFSchema):

    email = c.SchemaNode(
        widget=deform.widget.TextInputWidget(template="textinput_placeholder", type="email",  placeholder="Type in your email here"),

def get_newsletter_form(request):
    schema = SubscribeNewsLetter().bind(request=request)
    form = deform.Form(schema, buttons=("subscribe", ))
    return form

@simple_route("/newsletter", route_name="newsletter", renderer='views/newsletter.html', append_slash=False)
def newsletter(request):
    """Subscribe to news letter from handling."""

    form = get_newsletter_form(request)
    rendered_form = form.render()

    if request.method == "POST":

        # Limit to 60 subscriptions / hour
        limit = int(request.registry.settings.get("trees.newsletter_subscription_limit", 60))

        if rollingwindow.check(request.registry, "invite_friends", window=3600, limit=limit):
            # Alert devops through Sentry
            logger.warning("Newsletter subscription overflow")
            return HTTPTooManyRequests("Too many subscriptions to the newsletter. Please wait 10 minutes and try again.")

        if "subscribe" in request.POST:
            controls = request.POST.items()
                appstruct = form.validate(controls)

                email = appstruct["email"]
                referrer = request.referrer
                ip = request.client_addr

                subscription, created = NewsletterSubscriber.get_or_create_subscriber(email, referrer, ip)

                # Send email on subsequent submissions as there might have been failed email delivery
                send_templated_mail(request, [email], "views/email/welcome", locals())

                if created:
                   messages.add(request, kind="success", msg_id="msg-thank-ou", msg="Thank you! Check your inbox {} for information and coupon code.".format(email))
                    messages.add(request, kind="error", msg="Email address {} is already subscribed.".format(email), msg_id="#msg-already-subscribed")

            except deform.ValidationFailure as e:
                rendered_form = e.render()

    return locals()

Then you can bang it with the following functional test case:

import transaction

from trees.models import NewsletterSubscriber
from websauna.system.model import DBSession

def test_subscribe_newsletter(dbsession, web_server, browser):
    """Visitor can subscribe to a newsletter."""

    b = browser
    b.visit(web_server + "/newsletter")

    b.fill("email", "[email protected]")

    # Displayed as a message after succesful form subscription
    assert b.is_element_present_by_css("#msg-thank-you")

    # Check we get an entry
    with transaction.manager:
        assert DBSession.query(NewsletterSubscriber).count() == 1
        subscription = DBSession.query(NewsletterSubscriber).first()
        assert == "[email protected]"
        assert subscription.ip == ""

def test_subscribe_newsletter_twice(dbsession, web_server, browser):
    """The second newsletter subscription attempt gives error message."""

    b = browser
    b.visit(web_server + "/newsletter")
    b.fill("email", "[email protected]")

    # And again
    b.visit(web_server + "/newsletter")
    b.fill("email", "[email protected]")

    # Error message displayed if the user tries to subscribe twice
    assert b.is_element_present_by_css("#msg-already-subscribed")

    # Check we don't get double entry
    with transaction.manager:
        assert DBSession.query(NewsletterSubscriber).count() == 1