commit
4218359bac
@ -3,7 +3,7 @@
|
|||||||
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
|
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
|
||||||
"lines": null
|
"lines": null
|
||||||
},
|
},
|
||||||
"generated_at": "2019-11-01T18:50:54Z",
|
"generated_at": "2019-11-06T21:35:23Z",
|
||||||
"plugins_used": [
|
"plugins_used": [
|
||||||
{
|
{
|
||||||
"base64_limit": 4.5,
|
"base64_limit": 4.5,
|
||||||
@ -170,15 +170,6 @@
|
|||||||
"type": "Private Key"
|
"type": "Private Key"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tests/forms/test_validators.py": [
|
|
||||||
{
|
|
||||||
"hashed_secret": "260408f687da9094705a841acda9b029563053ee",
|
|
||||||
"is_secret": false,
|
|
||||||
"is_verified": false,
|
|
||||||
"line_number": 30,
|
|
||||||
"type": "Hex High Entropy String"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tests/routes/task_orders/test_new.py": [
|
"tests/routes/task_orders/test_new.py": [
|
||||||
{
|
{
|
||||||
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from .forms import BaseForm
|
from .forms import BaseForm, remove_empty_string
|
||||||
from wtforms.fields import StringField, TextAreaField, FieldList
|
from wtforms.fields import StringField, TextAreaField, FieldList
|
||||||
from wtforms.validators import Required, Optional
|
from wtforms.validators import Required, Optional
|
||||||
from atst.forms.validators import ListItemRequired, ListItemsUnique
|
from atst.forms.validators import ListItemRequired, ListItemsUnique
|
||||||
@ -7,24 +7,31 @@ from atst.utils.localization import translate
|
|||||||
|
|
||||||
class EditEnvironmentForm(BaseForm):
|
class EditEnvironmentForm(BaseForm):
|
||||||
name = StringField(
|
name = StringField(
|
||||||
label=translate("forms.environments.name_label"), validators=[Required()]
|
label=translate("forms.environments.name_label"),
|
||||||
|
validators=[Required()],
|
||||||
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class NameAndDescriptionForm(BaseForm):
|
class NameAndDescriptionForm(BaseForm):
|
||||||
name = StringField(
|
name = StringField(
|
||||||
label=translate("forms.application.name_label"), validators=[Required()]
|
label=translate("forms.application.name_label"),
|
||||||
|
validators=[Required()],
|
||||||
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
description = TextAreaField(
|
description = TextAreaField(
|
||||||
label=translate("forms.application.description_label"),
|
label=translate("forms.application.description_label"),
|
||||||
validators=[Optional()],
|
validators=[Optional()],
|
||||||
filters=[lambda x: x or None],
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnvironmentsForm(BaseForm):
|
class EnvironmentsForm(BaseForm):
|
||||||
environment_names = FieldList(
|
environment_names = FieldList(
|
||||||
StringField(label=translate("forms.application.environment_names_label")),
|
StringField(
|
||||||
|
label=translate("forms.application.environment_names_label"),
|
||||||
|
filters=[remove_empty_string],
|
||||||
|
),
|
||||||
validators=[
|
validators=[
|
||||||
ListItemRequired(
|
ListItemRequired(
|
||||||
message=translate(
|
message=translate(
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask import current_app, request as http_request
|
from flask import current_app, request as http_request
|
||||||
|
import re
|
||||||
|
|
||||||
from atst.utils.flash import formatted_flash as flash
|
from atst.utils.flash import formatted_flash as flash
|
||||||
|
|
||||||
EMPTY_LIST_FIELD = ["", None]
|
EMPTY_LIST_FIELD = ["", None]
|
||||||
|
|
||||||
|
|
||||||
|
def remove_empty_string(value):
|
||||||
|
# only return strings that contain non whitespace characters
|
||||||
|
if value and re.search(r"\S", value):
|
||||||
|
return value.strip()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class BaseForm(FlaskForm):
|
class BaseForm(FlaskForm):
|
||||||
def __init__(self, formdata=None, **kwargs):
|
def __init__(self, formdata=None, **kwargs):
|
||||||
# initialize the form with data from the cache
|
# initialize the form with data from the cache
|
||||||
@ -35,7 +44,3 @@ class BaseForm(FlaskForm):
|
|||||||
if not valid and flash_invalid:
|
if not valid and flash_invalid:
|
||||||
flash("form_errors")
|
flash("form_errors")
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remove_empty_string(cls, value):
|
|
||||||
return value or None
|
|
||||||
|
@ -8,7 +8,7 @@ from wtforms.fields import (
|
|||||||
from wtforms.validators import Length, Optional
|
from wtforms.validators import Length, Optional
|
||||||
from wtforms.widgets import ListWidget, CheckboxInput
|
from wtforms.widgets import ListWidget, CheckboxInput
|
||||||
|
|
||||||
from .forms import BaseForm
|
from .forms import BaseForm, remove_empty_string
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
from .data import (
|
from .data import (
|
||||||
@ -49,7 +49,7 @@ class PortfolioCreationForm(BaseForm):
|
|||||||
translate("forms.task_order.defense_component_label"),
|
translate("forms.task_order.defense_component_label"),
|
||||||
choices=SERVICE_BRANCHES,
|
choices=SERVICE_BRANCHES,
|
||||||
default="",
|
default="",
|
||||||
filters=[BaseForm.remove_empty_string],
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
|
|
||||||
description = TextAreaField(
|
description = TextAreaField(
|
||||||
@ -78,7 +78,6 @@ class PortfolioCreationForm(BaseForm):
|
|||||||
description=translate("forms.task_order.complexity.description"),
|
description=translate("forms.task_order.complexity.description"),
|
||||||
choices=APPLICATION_COMPLEXITY,
|
choices=APPLICATION_COMPLEXITY,
|
||||||
default=None,
|
default=None,
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
widget=ListWidget(prefix_label=False),
|
widget=ListWidget(prefix_label=False),
|
||||||
option_widget=CheckboxInput(),
|
option_widget=CheckboxInput(),
|
||||||
)
|
)
|
||||||
@ -86,7 +85,7 @@ class PortfolioCreationForm(BaseForm):
|
|||||||
complexity_other = StringField(
|
complexity_other = StringField(
|
||||||
translate("forms.task_order.complexity_other_label"),
|
translate("forms.task_order.complexity_other_label"),
|
||||||
default=None,
|
default=None,
|
||||||
filters=[BaseForm.remove_empty_string],
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
|
|
||||||
dev_team = SelectMultipleField(
|
dev_team = SelectMultipleField(
|
||||||
@ -94,7 +93,6 @@ class PortfolioCreationForm(BaseForm):
|
|||||||
description=translate("forms.task_order.dev_team.description"),
|
description=translate("forms.task_order.dev_team.description"),
|
||||||
choices=DEV_TEAM,
|
choices=DEV_TEAM,
|
||||||
default=None,
|
default=None,
|
||||||
filters=[BaseForm.remove_empty_string],
|
|
||||||
widget=ListWidget(prefix_label=False),
|
widget=ListWidget(prefix_label=False),
|
||||||
option_widget=CheckboxInput(),
|
option_widget=CheckboxInput(),
|
||||||
)
|
)
|
||||||
@ -102,7 +100,7 @@ class PortfolioCreationForm(BaseForm):
|
|||||||
dev_team_other = StringField(
|
dev_team_other = StringField(
|
||||||
translate("forms.task_order.dev_team_other_label"),
|
translate("forms.task_order.dev_team_other_label"),
|
||||||
default=None,
|
default=None,
|
||||||
filters=[BaseForm.remove_empty_string],
|
filters=[remove_empty_string],
|
||||||
)
|
)
|
||||||
|
|
||||||
team_experience = RadioField(
|
team_experience = RadioField(
|
||||||
|
@ -65,10 +65,12 @@ def Name(message=translate("forms.validators.name_message")):
|
|||||||
|
|
||||||
def ListItemRequired(
|
def ListItemRequired(
|
||||||
message=translate("forms.validators.list_item_required_message"),
|
message=translate("forms.validators.list_item_required_message"),
|
||||||
empty_values=("", None),
|
empty_values=[None],
|
||||||
):
|
):
|
||||||
def _list_item_required(form, field):
|
def _list_item_required(form, field):
|
||||||
non_empty_values = [v for v in field.data if v not in empty_values]
|
non_empty_values = [
|
||||||
|
v for v in field.data if (v not in empty_values and re.search(r"\S", v))
|
||||||
|
]
|
||||||
if len(non_empty_values) == 0:
|
if len(non_empty_values) == 0:
|
||||||
raise ValidationError(message)
|
raise ValidationError(message)
|
||||||
|
|
||||||
|
@ -91,7 +91,9 @@ export default {
|
|||||||
if (this.environments.length > 1) {
|
if (this.environments.length > 1) {
|
||||||
// only want to display this error if we have multiple envs and one or
|
// only want to display this error if we have multiple envs and one or
|
||||||
// more do not have names
|
// more do not have names
|
||||||
return this.environments.every(e => e.name !== '')
|
return this.environments.every(
|
||||||
|
e => !e.name.match(/^\s+$/) && e.name !== ''
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from wtforms.fields import RadioField
|
from wtforms.fields import RadioField, FieldList, StringField
|
||||||
from werkzeug.datastructures import ImmutableMultiDict
|
from werkzeug.datastructures import ImmutableMultiDict
|
||||||
|
|
||||||
from atst.forms.forms import BaseForm
|
from atst.forms.forms import BaseForm, remove_empty_string
|
||||||
|
|
||||||
|
|
||||||
class FormWithChoices(BaseForm):
|
class FormWithChoices(BaseForm):
|
||||||
@ -16,6 +16,12 @@ class FormWithChoices(BaseForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FormWithList(BaseForm):
|
||||||
|
fancy_list = FieldList(
|
||||||
|
StringField("a very fancy list", filters=[remove_empty_string])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestBaseForm:
|
class TestBaseForm:
|
||||||
class Foo:
|
class Foo:
|
||||||
person = {"force_side": None}
|
person = {"force_side": None}
|
||||||
@ -34,3 +40,17 @@ class TestBaseForm:
|
|||||||
form_data_3 = ImmutableMultiDict({"force_side": "dark"})
|
form_data_3 = ImmutableMultiDict({"force_side": "dark"})
|
||||||
form_3 = FormWithChoices(form_data_3, obj=self.obj)
|
form_3 = FormWithChoices(form_data_3, obj=self.obj)
|
||||||
assert form_3.data["force_side"] is "dark"
|
assert form_3.data["force_side"] is "dark"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"form_data",
|
||||||
|
[["testing", "", "QA"], ["testing", " ", "QA"], ["testing", None, "QA"]],
|
||||||
|
)
|
||||||
|
def test_blank_list_items_removed(self, form_data):
|
||||||
|
form = FormWithList(fancy_list=form_data)
|
||||||
|
assert form.validate(flash_invalid=False)
|
||||||
|
assert not form.data == ["testing", "QA"]
|
||||||
|
|
||||||
|
def test_remove_empty_string_clips_whitespace(self):
|
||||||
|
form = FormWithList(fancy_list=[" QA", " testing "])
|
||||||
|
assert form.validate(flash_invalid=False)
|
||||||
|
assert form.fancy_list.data == ["QA", "testing"]
|
||||||
|
@ -85,3 +85,20 @@ class TestFileLength:
|
|||||||
|
|
||||||
dummy_field.data = "random string"
|
dummy_field.data = "random string"
|
||||||
assert validator(dummy_form, dummy_field)
|
assert validator(dummy_form, dummy_field)
|
||||||
|
|
||||||
|
|
||||||
|
class TestListItemRequired:
|
||||||
|
@pytest.mark.parametrize("valid", [[" a", ""], ["a ", ""], ["a", ""]])
|
||||||
|
def test_ListItemRequired(self, valid, dummy_form, dummy_field):
|
||||||
|
validator = ListItemRequired()
|
||||||
|
dummy_field.data = valid
|
||||||
|
validator(dummy_form, dummy_field)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("invalid", [[""], [" "], [None], []])
|
||||||
|
def test_ListItemRequired_rejects_blank_names(
|
||||||
|
self, invalid, dummy_form, dummy_field
|
||||||
|
):
|
||||||
|
validator = ListItemRequired()
|
||||||
|
dummy_field.data = invalid
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
validator(dummy_form, dummy_field)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user