Merge pull request #370 from alphagov/add-tour

Make first time use clearer
This commit is contained in:
Chris Hill-Scott
2016-04-04 13:40:37 +01:00
30 changed files with 343 additions and 212 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -7,6 +7,25 @@
const tagPattern = /\(\([^\)\(]+\)\)/g; const tagPattern = /\(\([^\)\(]+\)\)/g;
const getPlaceholderHint = function(placeholders) {
if (0 === placeholders.length) {
return `
<p>Add fields using ((double brackets))</p>
<span class='placeholder-hint-action' tabindex='0' role='button'>Show me how</span>
`;
}
if (1 === placeholders.length) {
return `
<p>Add fields using ((double brackets))</p>
<p>Youll populate the ${placeholders[0]} field when you send messages using this template</p>
`;
}
return `
<p>Add fields using ((double brackets))</p>
<p>Youll populate your fields when you send some messages</p>
`;
};
Modules.HighlightTags = function() { Modules.HighlightTags = function() {
this.start = function(textarea) { this.start = function(textarea) {
@@ -22,6 +41,9 @@
`)) `))
.on("input", this.update); .on("input", this.update);
this.$placeHolderHint = $('#placeholder-hint')
.on("click", ".placeholder-hint-action", this.demo);
this.initialHeight = this.$textbox.height(); this.initialHeight = this.$textbox.height();
this.$backgroundMaskForeground.width( this.$backgroundMaskForeground.width(
@@ -40,14 +62,30 @@
) )
); );
this.escapedMessage = () => $('<div/>').text(this.$textbox.val()).html();
this.listPlaceholders = () => this.escapedMessage().match(tagPattern) || [];
this.listPlaceholdersWithoutBrackets = () => this.listPlaceholders().map(
placeholder => placeholder.substring(2, placeholder.length - 2)
);
this.replacePlaceholders = () => this.$backgroundMaskForeground.html( this.replacePlaceholders = () => this.$backgroundMaskForeground.html(
$('<div/>').text(this.$textbox.val()).html().replace( this.escapedMessage().replace(
tagPattern, match => `<span class='tag'>${match}</span>` tagPattern, match => `<span class='tag'>${match}</span>`
) )
); );
this.hint = () => this.$placeHolderHint.html(
getPlaceholderHint(this.listPlaceholdersWithoutBrackets())
);
this.update = () => ( this.update = () => (
this.replacePlaceholders() && this.resize() this.replacePlaceholders() && this.resize() && this.hint()
);
this.demo = () => (
this.$textbox.val((i, current) => `Dear ((name)), ${current}`) && this.update()
); );
}; };

View File

@@ -58,65 +58,60 @@
} }
%banner-tip,
.banner-tip {
@extend %banner;
@include bold-19;
background-color: $yellow;
color: $text-colour;
text-align: left;
margin-top: 0;
a {
&:link,
&:visited {
color: $text-colour;
text-decoration: underline;
}
}
ol {
list-style-type: decimal;
}
}
.banner-tip-with-tick {
@extend %banner-with-tick;
@extend %banner-tip;
background-image: file-url('tick-black.png');
}
.banner-info,
.banner-important {
@extend %banner;
background: $white;
color: $text-colour;
background-image: file-url('icon-important-2x.png');
background-size: 34px 34px;
background-position: 0 0;
background-repeat: no-repeat;
padding: 7px 0 5px 50px;
}
.banner-info {
background-image: file-url('icon-information-2x.png');
}
.banner-mode { .banner-mode {
@extend %banner; @extend %banner;
background: $govuk-blue; background: $govuk-blue;
color: $white; color: $white;
margin-top: $gutter; margin-top: $gutter;
padding: $gutter-half;
.heading-medium { p {
margin: 0 0 10px 0; margin: 0 0 10px 0;
} }
&-action {
text-align: right;
}
}
.banner-tour {
@extend %banner;
background: $govuk-blue;
color: $white;
margin-top: $gutter;
padding: $gutter * 2;
.heading-large {
margin: 0 0 $gutter 0;
}
p {
margin-bottom: $gutter;
&:last-child {
margin-bottom: 0
}
& + p {
margin-top: -$gutter-half;
}
}
a { a {
@include bold-24;
display: inline-block;
background-image: file-url('tour-next.png');
background-size: auto 24px;
padding: 0 23px 0 0;
background-position: right 3px;
background-repeat: no-repeat;
&:link, &:link,
&:visited { &:visited {
color: $white; color: $white;
@@ -124,19 +119,25 @@
&:hover, &:hover,
&:active { &:active {
color: $light-blue-25; background-color: $link-hover-colour;
outline: 10px solid $link-hover-colour;
}
&:active,
&:focus {
background-color: $yellow;
outline: 10px solid $yellow;
} }
} }
.big-number { img {
max-width: 100%;
margin-top: 10px; display: block;
}
&-label {
padding-bottom: 0;
}
&-image-flush-bottom {
margin: 40px 0 -60px 0;
} }
} }

View File

@@ -1,6 +1,6 @@
.textbox-highlight { .textbox-highlight {
$tag-background: rgba($light-blue, 0.7); $tag-background: rgba($light-blue, 0.6);
&-wrapper { &-wrapper {
position: relative; position: relative;

View File

@@ -1,9 +1,23 @@
.edit-template { .placeholder-hint {
display: block;
&-title {
@include bold-19;
}
&-action {
@include bold-19;
display: inline-block;
text-decoration: underline;
cursor: pointer;
&:active {
background: $yellow-25;
outline: none;
}
&-placeholder-hint {
display: block;
padding-top: 20px;
//color: $secondary-text-colour;
} }
} }

View File

@@ -23,5 +23,6 @@ from app.main.views import (
api_keys, api_keys,
manage_users, manage_users,
invites, invites,
all_services all_services,
tour
) )

View File

@@ -21,7 +21,7 @@ from app.main.validators import (Blacklist, CsvFileValidator, ValidEmailDomainRe
def email_address(label='Email address'): def email_address(label='Email address'):
return EmailField(label, validators=[ return EmailField(label, validators=[
Length(min=5, max=255), Length(min=5, max=255),
DataRequired(message='Email cannot be empty'), DataRequired(message='Cant be empty'),
Email(message='Enter a valid email address'), Email(message='Enter a valid email address'),
ValidEmailDomainRegex()]) ValidEmailDomainRegex()])
@@ -37,35 +37,35 @@ class UKMobileNumber(TelField):
def mobile_number(): def mobile_number():
return UKMobileNumber('Mobile phone number', return UKMobileNumber('Mobile phone number',
validators=[DataRequired(message='Cannot be empty')]) validators=[DataRequired(message='Cant be empty')])
def password(label='Create a password'): def password(label='Create a password'):
return PasswordField(label, return PasswordField(label,
validators=[DataRequired(message='Password can not be empty'), validators=[DataRequired(message='Cant be empty'),
Length(10, 255, message='Password must be at least 10 characters'), Length(10, 255, message='Must be at least 10 characters'),
Blacklist(message='That password is blacklisted, too common')]) Blacklist(message='That password is blacklisted, too common')])
def sms_code(): def sms_code():
verify_code = '^\d{5}$' verify_code = '^\d{5}$'
return StringField('Text message code', return StringField('Text message code',
validators=[DataRequired(message='Text message confirmation code can not be empty'), validators=[DataRequired(message='Cant be empty'),
Regexp(regex=verify_code, Regexp(regex=verify_code,
message='Text message confirmation code must be 5 digits')]) message='Must be 5 digits')])
def email_code(): def email_code():
verify_code = '^\d{5}$' verify_code = '^\d{5}$'
return StringField("Email code", return StringField("Email code",
validators=[DataRequired(message='Email confirmation code can not be empty'), validators=[DataRequired(message='Cant be empty'),
Regexp(regex=verify_code, message='Email confirmation code must be 5 digits')]) Regexp(regex=verify_code, message='Must be 5 digits')])
class LoginForm(Form): class LoginForm(Form):
email_address = StringField('Email address', validators=[ email_address = StringField('Email address', validators=[
Length(min=5, max=255), Length(min=5, max=255),
DataRequired(message='Email cannot be empty'), DataRequired(message='Cant be empty'),
Email(message='Enter a valid email address') Email(message='Enter a valid email address')
]) ])
password = PasswordField('Password', validators=[ password = PasswordField('Password', validators=[
@@ -76,7 +76,7 @@ class LoginForm(Form):
class RegisterUserForm(Form): class RegisterUserForm(Form):
name = StringField('Full name', name = StringField('Full name',
validators=[DataRequired(message='Name can not be empty')]) validators=[DataRequired(message='Cant be empty')])
email_address = email_address() email_address = email_address()
mobile_number = mobile_number() mobile_number = mobile_number()
password = password() password = password()
@@ -84,7 +84,7 @@ class RegisterUserForm(Form):
class RegisterUserFromInviteForm(Form): class RegisterUserFromInviteForm(Form):
name = StringField('Full name', name = StringField('Full name',
validators=[DataRequired(message='Name can not be empty')]) validators=[DataRequired(message='Cant be empty')])
mobile_number = mobile_number() mobile_number = mobile_number()
password = password() password = password()
service = HiddenField('service') service = HiddenField('service')
@@ -108,7 +108,7 @@ class InviteUserForm(PermissionsForm):
def validate_email_address(self, field): def validate_email_address(self, field):
if field.data.lower() == self.invalid_email_address: if field.data.lower() == self.invalid_email_address:
raise ValidationError("You can't send an invitation to yourself") raise ValidationError("You cant send an invitation to yourself")
class TwoFactorForm(Form): class TwoFactorForm(Form):
@@ -149,7 +149,7 @@ class AddServiceForm(Form):
name = StringField( name = StringField(
'Service name', 'Service name',
validators=[ validators=[
DataRequired(message='Service name cant be empty') DataRequired(message='Cant be empty')
] ]
) )
@@ -173,7 +173,7 @@ class ServiceNameForm(Form):
name = StringField( name = StringField(
u'New name', u'New name',
validators=[ validators=[
DataRequired(message='Service name cant be empty') DataRequired(message='Cant be empty')
]) ])
def validate_name(self, a): def validate_name(self, a):
@@ -199,18 +199,18 @@ class ConfirmPasswordForm(Form):
class SMSTemplateForm(Form): class SMSTemplateForm(Form):
name = StringField( name = StringField(
u'Template name', u'Template name',
validators=[DataRequired(message="Template name cannot be empty")]) validators=[DataRequired(message="Cant be empty")])
template_content = TextAreaField( template_content = TextAreaField(
u'Message', u'Message content',
validators=[DataRequired(message="Template content cannot be empty")]) validators=[DataRequired(message="Cant be empty")])
class EmailTemplateForm(SMSTemplateForm): class EmailTemplateForm(SMSTemplateForm):
subject = StringField( subject = StringField(
u'Subject', u'Subject',
validators=[DataRequired(message="Subject cannot be empty")]) validators=[DataRequired(message="Cant be empty")])
class ForgotPasswordForm(Form): class ForgotPasswordForm(Form):

View File

@@ -44,7 +44,7 @@ def add_service():
user_id=session['user_id'], user_id=session['user_id'],
email_from=email_from) email_from=email_from)
return redirect(url_for('main.service_dashboard', service_id=service_id)) return redirect(url_for('main.tour', service_id=service_id, page=1))
else: else:
return render_template( return render_template(
'views/add-service.html', 'views/add-service.html',

View File

@@ -77,7 +77,6 @@ def choose_template(service_id, template_type):
if template_type not in ['email', 'sms']: if template_type not in ['email', 'sms']:
abort(404) abort(404)
jobs = job_api_client.get_job(service_id)['data']
return render_template( return render_template(
'views/choose-template.html', 'views/choose-template.html',
@@ -91,7 +90,6 @@ def choose_template(service_id, template_type):
template_type=template_type, template_type=template_type,
page_heading=get_page_headings(template_type), page_heading=get_page_headings(template_type),
service=service, service=service,
has_jobs=len(jobs),
service_id=service_id service_id=service_id
) )

15
app/main/views/tour.py Normal file
View File

@@ -0,0 +1,15 @@
from flask import render_template
from flask_login import login_required
from app.main import main
@main.route("/services/<service_id>/tour/<int:page>")
@login_required
def tour(service_id, page):
return render_template(
'views/tour/{}.html'.format(page),
service_id=service_id, # TODO: fix when Nicks PR is merged
current_page=page,
next_page=(page + 1)
)

View File

@@ -15,25 +15,6 @@
When people receive notifications, who should they be from? When people receive notifications, who should they be from?
</h1> </h1>
<p>
Be specific to your service. Remember that there might be
other people in your organisation using GOV.UK Notify.
</p>
<p>
Users will see this:
</p>
<ul class="list-bullet bottom-gutter">
<li>
at the start of every text message, eg Vehicle tax: we received your
payment, thank you
</li>
<li>
as your email sender name
</li>
</ul>
<form autocomplete="off" method="post"> <form autocomplete="off" method="post">
{{ textbox(form.name, hint="You can change this later") }} {{ textbox(form.name, hint="You can change this later") }}

View File

@@ -22,13 +22,6 @@
developer documentation</a>. developer documentation</a>.
</p> </p>
{{ banner(
'You can only send messages to yourself until you <a href="{}">request to go live</a>'.format(
url_for('.service_request_to_go_live', service_id=service_id)
)|safe,
type='important'
) }}
<h2 class="api-key-name"> <h2 class="api-key-name">
Service ID Service ID
</h2> </h2>

View File

@@ -37,17 +37,6 @@
{% endif %} {% endif %}
</div> </div>
{% if not has_jobs %}
{% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'], any_=True) %}
{{ banner(
"""
Send yourself a test
""",
subhead='Next step:',
type="tip"
)}}
{% endif %}
{% endif %}
<div class="grid-row"> <div class="grid-row">
{% for template in templates %} {% for template in templates %}
<div class="column-two-thirds"> <div class="column-two-thirds">

View File

@@ -6,25 +6,25 @@
{% block maincolumn_content %} {% block maincolumn_content %}
{% if service.restricted %} {% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
{% include 'views/dashboard/trial-mode-banner.html' %} {% include 'views/dashboard/get-started.html' %}
{% endif %} {% elif service.restricted %}
<div class="dashboard">
{% include 'views/dashboard/trial-mode-banner.html' %}
</div>
{% endif %}
{% if not templates and current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], any_=True) %}
{% include 'views/dashboard/get-started.html' %}
{% endif %}
{% if templates %} <div
<div data-module="update-content"
data-module="update-content" data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}"
data-resource="{{url_for(".service_dashboard_updates", service_id=service_id)}}" data-key="today"
data-key="today" data-interval-seconds="10"
data-interval-seconds="10" >
> {% include 'views/dashboard/today.html' %}
{% include 'views/dashboard/today.html' %} </div>
</div>
{% include 'views/dashboard/jobs.html' %}
{% include 'views/dashboard/jobs.html' %}
{% endif %}
{% endblock %} {% endblock %}

View File

@@ -1,30 +1,23 @@
{% from "components/banner.html" import banner_wrapper %} {% from "components/banner.html" import banner_wrapper %}
<h2 class="heading-medium">Get started</h2>
{% if current_user.has_permissions(['manage_templates']) %} {% if current_user.has_permissions(['manage_templates']) %}
<p> {% call banner_wrapper(type='tour') %}
You need to set up a template before you can send messages <h2 class="heading-large">Youre ready to get started</h2>
</p> <p>
<ol class="grid-row"> <a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}'>
<li class="column-half"> Set up a text message template
{% call banner_wrapper(type="tip") %} </a>
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}' style="white-space: nowrap;"> </p>
Set up a text message template <p>
</a> <a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
{% endcall %} Set up an email template
</li> </a>
<li class="column-half"> </p>
{% call banner_wrapper(type="tip") %} {% endcall %}
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="email") }}'>
Set up an email template
</a>
{% endcall %}
</li>
</ol>
{% else %} {% else %}
<p> {% call banner_wrapper(type='mode') %}
<p> <p>
You need to ask your service manager to set up some templates before you can send messages You need to ask your service manager to set up some templates before you can send messages
</p> </p>
</p> {% endcall %}
{% endif %} {% endif %}

View File

@@ -2,24 +2,12 @@
{% from "components/big-number.html" import big_number %} {% from "components/big-number.html" import big_number %}
{% call banner_wrapper(type="mode") %} {% call banner_wrapper(type="mode") %}
<h2 class="heading-medium">Trial mode</h2>
<div class="grid-row"> <div class="grid-row">
<div class="column-one-half"> <div class="column-one-half">
<p> Your service is in trial mode
Well only deliver messages to you and members of your team
<br>
<a href="{{ url_for(".help", _anchor="trial-mode") }}">Find out more</a>
</p>
</div> </div>
<div class="column-one-sixth"> <div class="column-one-half">
&nbsp; <a href="{{ url_for(".help", _anchor="trial-mode") }}" class="banner-mode-action">Find out more</a>
</div>
<div class="column-one-third">
{{ big_number(
service.limit - statistics.get('emails_requested', 0) - statistics.get('sms_requested', 0),
'messages left today'
) }}
</div> </div>
</div> </div>
{% endcall %} {% endcall %}

View File

@@ -20,23 +20,20 @@
</div> </div>
<div class="column-two-thirds"> <div class="column-two-thirds">
{{ textbox(form.template_content, highlight_tags=True, width='1-1') }} {{ textbox(form.template_content, highlight_tags=True, width='1-1') }}
{{ page_footer(
'Save',
delete_link=url_for('.delete_service_template', service_id=service_id, template_id=template_id) if template_id or None,
delete_link_text='Delete this template'
) }}
</div> </div>
<div class="column-one-third"> <div class="column-one-third">
<label for='template_content' class='edit-template-placeholder-hint'> <label for='template_content' class='placeholder-hint'>
<p> <div class="banner-mode" id="placeholder-hint" aria-live="polite">
Add personalisation with double brackets, eg Dear ((name)) <p>Add fields using ((double brackets))</p>
</p> </div>
<p>
Youll fill in the real data when you send a message.
</p>
</label> </label>
</div> </div>
</div> </div>
{{ page_footer(
'Save',
delete_link=url_for('.delete_service_template', service_id=service_id, template_id=template_id) if template_id or None,
delete_link_text='Delete this template'
) }}
</form> </form>

View File

@@ -19,23 +19,20 @@
</div> </div>
<div class="column-two-thirds"> <div class="column-two-thirds">
{{ textbox(form.template_content, highlight_tags=True, width='1-1') }} {{ textbox(form.template_content, highlight_tags=True, width='1-1') }}
{{ page_footer(
'Save',
delete_link=url_for('.delete_service_template', service_id=service_id, template_id=template_id) if template_id or None,
delete_link_text='Delete this template'
) }}
</div> </div>
<div class="column-one-third"> <div class="column-one-third">
<label for='template_content' class='edit-template-placeholder-hint'> <label for='template_content' class='placeholder-hint'>
<p> <div class="banner-mode" id="placeholder-hint" aria-live="polite">
Add personalisation with double brackets, eg Dear ((name)) <p>Add fields using ((double brackets))</p>
</p> </div>
<p>
You'll have to add the real data when you send a message.
</p>
</label> </label>
</div> </div>
</div> </div>
{{ page_footer(
'Save',
delete_link=url_for('.delete_service_template', service_id=service_id, template_id=template_id) if template_id or None,
delete_link_text='Delete this template'
) }}
</form> </form>

View File

@@ -36,13 +36,6 @@
{{ banner('Are you sure you want to delete?', 'dangerous', delete_button="Yes, delete this thing")}} {{ banner('Are you sure you want to delete?', 'dangerous', delete_button="Yes, delete this thing")}}
{{ banner(
'<a href="#">Send your first message</a>'|safe,
subhead='Get started',
type='tip'
)}}
{{ banner('You could go to jail', 'important')}}
</div> </div>
</div> </div>

View File

@@ -0,0 +1,25 @@
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/banner.html" import banner_wrapper %}
{% block page_title %}
{{heading}} GOV.UK Notify
{% endblock %}
{% block maincolumn_content %}
{% call banner_wrapper(type='tour') %}
<h2 class="heading-large">Trial mode</h2>
<p>
To start off with, you can only send messages to yourself.
</p>
<p>
We can remove these restrictions when youre ready.
</p>
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
Next
</a>
{% endcall %}
{% endblock %}

View File

@@ -0,0 +1,29 @@
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/banner.html" import banner_wrapper %}
{% block page_title %}
{{heading}} GOV.UK Notify
{% endblock %}
{% block maincolumn_content %}
{% call banner_wrapper(type='tour') %}
<h2 class="heading-large">Start with templates</h2>
<p>
Set up a template like this:
</p>
<p>
<img
src="/static/images/tour/{{ current_page }}.png"
width="554" height="97"
alt="A template for a text message with placeholders for the recipients name, document and date"
>
</p>
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
Next
</a>
{% endcall %}
{% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/banner.html" import banner_wrapper %}
{% block page_title %}
{{heading}} GOV.UK Notify
{% endblock %}
{% block maincolumn_content %}
{% call banner_wrapper(type='tour') %}
<h2 class="heading-large">Add recipients</h2>
<p>
Add recipients by uploading a .csv spreadsheet:
</p>
<p>
<img
src="/static/images/tour/{{ current_page }}.png"
width="554" height="118"
alt="A screenshot of a spreadsheet containing data about three people"
>
</p>
<p>
Developers, you can add data automatically using an API
</p>
<a href='{{ url_for('.tour', service_id=service_id, page=next_page) }}'>
Next
</a>
{% endcall %}
{% endblock %}

View File

@@ -0,0 +1,28 @@
{% extends "withoutnav_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/banner.html" import banner_wrapper %}
{% block page_title %}
{{heading}} GOV.UK Notify
{% endblock %}
{% block maincolumn_content %}
{% call banner_wrapper(type='tour') %}
<h2 class="heading-large">Send your messages</h2>
<p>
Notify merges your data with the template and sends the messages
</p>
<a href="{{ url_for('.service_dashboard', service_id=service_id) }}">
Next
</a>
<img
src="/static/images/tour/{{ current_page }}.png"
class="banner-tour-image-flush-bottom"
width="840" height="290"
alt="Three mobiles phones, each showing a text message personalised with data about the recipient"
>
{% endcall %}
{% endblock %}

View File

@@ -26,7 +26,7 @@ def test_should_add_service_and_redirect_to_next_page(app_,
url_for('main.add_service'), url_for('main.add_service'),
data={'name': 'testing the post'}) data={'name': 'testing the post'})
assert response.status_code == 302 assert response.status_code == 302
assert response.location == url_for('main.service_dashboard', service_id=101, _external=True) assert response.location == url_for('main.tour', service_id=101, page=1, _external=True)
assert mock_get_services.called assert mock_get_services.called
mock_create_service.asset_called_once_with(service_name='testing the post', mock_create_service.asset_called_once_with(service_name='testing the post',
active=False, active=False,
@@ -44,7 +44,7 @@ def test_should_return_form_errors_when_service_name_is_empty(app_,
client.login(api_user_active, mocker) client.login(api_user_active, mocker)
response = client.post(url_for('main.add_service'), data={}) response = client.post(url_for('main.add_service'), data={})
assert response.status_code == 200 assert response.status_code == 200
assert 'Service name cant be empty' in response.get_data(as_text=True) assert 'Cant be empty' in response.get_data(as_text=True)
def test_should_return_form_errors_with_duplicate_service_name_regardless_of_case(app_, def test_should_return_form_errors_with_duplicate_service_name_regardless_of_case(app_,

View File

@@ -260,7 +260,7 @@ def test_user_cant_invite_themselves(
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
assert page.h1.string.strip() == 'Invite a team member' assert page.h1.string.strip() == 'Invite a team member'
form_error = page.find('span', class_='error-message').string.strip() form_error = page.find('span', class_='error-message').string.strip()
assert form_error == "You can't send an invitation to yourself" assert form_error == "You cant send an invitation to yourself"
assert not mock_create_invite.called assert not mock_create_invite.called

View File

@@ -0,0 +1,19 @@
import pytest
from flask import url_for
import app
@pytest.mark.parametrize("page", range(1, 5))
def test_should_render_tour_pages(
app_,
api_user_active,
mocker,
page
):
with app_.test_request_context():
with app_.test_client() as client:
client.login(api_user_active, mocker)
response = client.get(url_for('main.tour', service_id=101, page=page))
assert response.status_code == 200
assert 'Next' in response.get_data(as_text=True)