Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #432 - Give common problem types as a choice for the user #579

Merged
merged 16 commits into from
Mar 9, 2015
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Flask-Cache==0.13.1
Flask-Limiter==0.7.4
Flask-SQLAlchemy==1.0
GitHub-Flask==0.3.4
WTForms==1.0.5
WTForms==2.0.2
ua-parser==0.3.5
nose==1.3.1
blinker==1.3
29 changes: 22 additions & 7 deletions tests/functional/reporting.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,37 @@ define([
.get(require.toUrl(url + '?open=1'))
.findByCssSelector('#url').click()
.end()
.findByCssSelector('#summary').click()
.findByCssSelector('#browser').click()
.end()
.findByCssSelector('#url').type('hi')
.findByXpath('//*[@id="new-report"]/div/form/div[1]/div[2]/div[1]').getAttribute('class')
.then(function (className) {
assert.include(className, 'has-error');
assert.notInclude(className, 'no-error');
})
.end()
.findByCssSelector('#summary').click()
.findByCssSelector('#url').type('sup')
.end()
.findByCssSelector('.u-formGroup').getAttribute('class')
// xpath to the #url formGroup
.findByXpath('//*[@id="new-report"]/div/form/div[1]/div[2]/div[1]').getAttribute('class')
.then(function (className) {
assert.include(className, 'no-error');
assert.notInclude(className, 'has-error');
})
.end()
.findByCssSelector('#summary').type('sup')
// click in the textarea to trigger validation for radios
.findByCssSelector('#description').click()
.end()
.findByXpath('//*[@id="new-report"]/div/form/div[1]/div[1]/fieldset').getAttribute('class')
.then(function (className) {
assert.include(className, 'has-error');
assert.notInclude(className, 'no-error');
})
.end()
// pick a problem type
.findByCssSelector('#problem_category-0').click()
.end()
// xpath to the #summary formGroup
.findByXpath('//*[@id="new-report"]/div/form/div[1]/div[2]/div').getAttribute('class')
// validation message should be removed now
.findByXpath('//*[@id="new-report"]/div/form/div[1]/div[1]/fieldset').getAttribute('class')
.then(function (className) {
assert.include(className, 'no-error');
assert.notInclude(className, 'has-error');
Expand Down
75 changes: 37 additions & 38 deletions webcompat/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,22 @@
from wtforms import TextAreaField
from wtforms.validators import Length
from wtforms.validators import Optional
from wtforms.validators import Required
from wtforms.validators import InputRequired

This comment was marked as abuse.

This comment was marked as abuse.


AUTH_REPORT = 'github-auth-report'
PROXY_REPORT = 'github-proxy-report'
SCHEMES = ('http://', 'https://')

owner_choices = [(u'True', u'Yes'), (u'False', u'No')]
problem_choices = [(u'browser_bug', u'Looks like the browser has a bug'),
(u'site_bug', u'Looks like the website has a bug.'),
(u'unknown_bug', u'Don\'t know but something\'s wrong.')]
problem_choices = [
(u'detection_bug', u'Desktop site instead of mobile site'),
(u'mobile_site_bug', u'Mobile site is not usable'),
(u'video_bug', u'Video doesn\'t play'),
(u'layout_bug', u'Layout is messed up'),
(u'text_bug', u'Text is not visible'),
(u'unknown_bug', u'Somethign else - I\'ll add details below')
]
url_message = u'A URL is required.'
summary_message = u'Please give a summary.'
radio_message = u'Problem type required.'
username_message = u'A valid username must be {0} characters long'.format(
random.randrange(0, 99))

Expand All @@ -41,19 +45,16 @@

class IssueForm(Form):
'''Define form fields and validation for our bug reporting form.'''
url = StringField(u'Site URL*', [Required(message=url_message)])
url = StringField(u'Site URL*', [InputRequired(message=url_message)])
browser = StringField(u'Browser / Version', [Optional()])
os = StringField(u'Operating System', [Optional()])
summary = StringField(u'Problem in 5 words*',
[Required(message=summary_message)])
username = StringField(u'Username',
[Length(max=0, message=username_message)])
description = TextAreaField(u'How can we replicate this?', [Optional()],
description = TextAreaField(u'Give more details', [Optional()],
default=desc_default)
site_owner = RadioField(u'Is this your website?', [Optional()],
choices=owner_choices)
problem_category = RadioField(u'What seems to be the trouble?',
[Optional()], choices=problem_choices)
problem_category = RadioField(u'What seems to be the trouble?*',

This comment was marked as abuse.

This comment was marked as abuse.

[InputRequired(message=radio_message)],
choices=problem_choices)


def get_problem(category):
Expand All @@ -65,14 +66,12 @@ def get_problem(category):
return u'Unknown'


def get_owner(is_site_owner):
'''Return human-readable language (Y/N) for site owner form value.'''
if is_site_owner == 'True':
return u'Yes'
elif is_site_owner == 'False':
return u'No'
def get_problem_summary(category):
'''Allows us to special case the "Other" radio choice summary.'''
if category == 'unknown_bug':
return u'see bug description'
else:
return u'Unknown'
return get_problem(category).lower()


def wrap_label(label):
Expand Down Expand Up @@ -127,7 +126,6 @@ def build_formdata(form_object):
URL -> part of body
Description -> part of body
Category -> labels
Owner -> labels

We'll try to parse the Browser and come up with a browser label, as well
as labels like mobile, desktop, tablet.
Expand All @@ -150,27 +148,28 @@ def build_formdata(form_object):
normalized_url = normalize_url(url)
# Domain extraction
domain = domain_name(normalized_url)
problem_summary = get_problem_summary(form_object.get('problem_category'))
if domain:
summary = '{0} - {1}'.format(domain, form_object.get('summary'))
summary = '{0} - {1}'.format(domain, problem_summary)
else:
summary = '{0}'.format(form_object.get('summary'))
summary = '{0} - {1}'.format(normalized_url, problem_summary)
# Preparing the body
body = u'''{0}{1}
**URL**: {2}
**Browser / Version**: {3}
**Operating System**: {4}
**Problem type**: {5}
**Site owner**: {6}
body = u'''{browser_label}{ua_label}
**URL**: {url}
**Browser / Version**: {browser}
**Operating System**: {os}
**Problem type**: {problem_type}

**Steps to Reproduce**
{7}'''.format(get_labels(form_object.get('browser')),
wrap_label(('ua_header', form_object.get('ua_header'))),
form_object.get('url'),
form_object.get('browser'),
form_object.get('os'),
get_problem(form_object.get('problem_category')),
get_owner(form_object.get('site_owner')),
form_object.get('description'))
{description}'''.format(
browser_label=get_labels(form_object.get('browser')),
ua_label=wrap_label(('ua_header', form_object.get('ua_header'))),
url=form_object.get('url'),
browser=form_object.get('browser'),
os=form_object.get('os'),
problem_type=get_problem(form_object.get('problem_category')),
description=form_object.get('description')
)
result = {}
result['title'] = summary
result['body'] = body
Expand Down
3 changes: 3 additions & 0 deletions webcompat/static/css/development/components/form.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ input[type="radio"] {
.u-formGroup {
margin-bottom:1.6em;
}
legend.u-formLabel {
width: 100%;
}
/* bootstrap overrides */
.radio-inline, .checkbox-inline {
/* Hack to get Chrome 24+ to behave.
Expand Down
59 changes: 39 additions & 20 deletions webcompat/static/js/lib/bugform.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
function BugForm() {
var urlField = $('#url');
var descField = $('#description');
var summaryField = $('#summary');
var problemType = $('[name=problem_category]');
var submitButtons = $('.Report-form button.Button');
var inputMap = {
'url': {
'elm': urlField, // elm is a jQuery object
'valid': false,
'valid': null,
'helpText': 'A URL is required.'
},
'summary' : {
'elm': summaryField,
'valid': false,
'helpText': 'Please give a summary.'
'problem_type': {
'elm': problemType,
'valid': null,
'helpText': 'Problem type required.'
}
};

Expand All @@ -25,8 +25,9 @@ function BugForm() {
self.checkParams();
urlField.on('input', self.copyURL);
self.disableSubmits();
urlField.on('blur input', self.checkValidity);
summaryField.on('blur input', self.checkValidity);
descField.on('focus', self.checkProblemTypeValidity);
problemType.on('change', self.checkProblemTypeValidity);
urlField.on('blur input', self.checkURLValidity);
},
checkParams: function() {
// Assumes a URI like: /?open=1&url=http://webpy.org/, for use by addons
Expand Down Expand Up @@ -60,40 +61,58 @@ function BugForm() {
submitButtons.prop('disabled', false);
submitButtons.removeClass('is-disabled');
},
/* Check to see that the form element is not empty.
checkProblemTypeValidity: function() {
if (!$('[name=problem_category]:checked').length) {
self.makeInvalid('problem_type');
} else {
self.makeValid('problem_type');
}
},
/* Check to see that the URL input element is not empty.
We don't do any other kind of validation yet. */
checkValidity: function(e) {
if ($.trim(e.target.value) === "") {
self.makeInvalid(e.target.id);
checkURLValidity: function() {
if ($.trim(urlField.val()) === "") {
self.makeInvalid('url');
} else {
self.makeValid(e.target.id);
self.makeValid('url');
}
},
makeInvalid: function(id) {
// Early return if inline help is already in place.
if (inputMap[id].elm.parent().prev('.help-inline').length) {
if (inputMap[id].valid === false) {
return;
}

var inlineHelp = $('<span></span>', {
'class': 'help-inline wc-bold',
'text': inputMap[id].helpText
});


inputMap[id].valid = false;
inputMap[id].elm.parents('.u-formGroup')
.removeClass('no-error')
.addClass('has-error');

$('<span></span>', {
'class': 'help-inline wc-bold',
'text': inputMap[id].helpText
}).insertAfter('label[for='+id+']');
if (id === 'url') {
inlineHelp.insertAfter('label[for='+id+']');
}

if (id === 'problem_type') {
inlineHelp.appendTo('legend.u-formLabel');
}

self.disableSubmits();
},
makeValid: function(id) {
inputMap[id].valid = true;
inputMap[id].elm.parents('.u-formGroup')
.removeClass('has-error')
.addClass('no-error');
inputMap[id].elm.prev('.help-inline').remove();

if (inputMap['url'].valid && inputMap['summary'].valid) {
inputMap[id].elm.parents('.u-formGroup').find('.help-inline').remove();

if (inputMap['url'].valid && inputMap['problem_type'].valid) {
self.enableSubmits();
}
},
Expand Down
Loading