Repository: mdn/kuma

Issue:
Require minium length for "explanation" field in BCD signals
**Summary**
_What should be changed?_
A minimum length of 10 characters should be required for the "explanation" field in BCD signals

**Rationale**
_What problems would this solve?_
Less spam submissions

**Audience**
_Who would use this changed feature?_
BCD maintainers

**Proposal**
_What would users see and do? What would happen as a result?_
Users would be required to enter a meaningful explanation and hopefully refrain from submitting "fehfs", "test", and other garbage.

**Additional context**
_Is there anything else we should know?_
Was discussed in https://github.com/mdn/sprints/issues/2289


Discussion:
@atopal Do we invoke the Open Participation team to come back and do this kind of maintenance or are we expected to do it ourselves from now on?
This is on us from now on Peter, they don't have any resources they can allocate to this anymore.
@Elchi3 I couldn't help but to start looking into it and now I'm almost there with a half-decent solution. 
Ideally, what I'd like to do is something where it displays (underneat the textarea) like "Minimum 10 characters. YOu have typed 8." And it'd vanish once you're above 10 and reappear again when you've typed too much and say "Maximum is 1,000 characters and you have typed 1,001."
But the code is rather complicated and hard to work on so doing that properly would be very hard. 

However, I found a decent middle-ground. You can set `minlength` and `maxlength` on a [`textarea`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea#Attributes) and if you combine that with the `required` attribute you can get something decent working. And instead of something fancy that pops up depending on the user typing, I just extended the help text that appears above with some more text. 

<img width="579" alt="Screen Shot 2019-11-20 at 4 06 25 PM" src="https://user-images.githubusercontent.com/26739/69278150-bc075180-0baf-11ea-9639-ac5bcbc85967.png">
*Note the extra sentence about (typo fixed) "Minimum %(min)s and maximum %(max)s characters."*

I also improved the logic that enables/disables the submit button so that it too respects this number. 

And if you type something >0 but <10 and then blur the input this happens:
<img width="584" alt="Screen Shot 2019-11-20 at 4 09 02 PM" src="https://user-images.githubusercontent.com/26739/69278333-1a343480-0bb0-11ea-919d-ab22180b2121.png">

And here's what happens if you type more than 10 characters and then blur the textarea:
<img width="587" alt="Screen Shot 2019-11-20 at 4 10 20 PM" src="https://user-images.githubusercontent.com/26739/69278405-4780e280-0bb0-11ea-88ba-3b21ecd18a00.png">

Implementing a minimum of 10 on the server (Django) is easy but it kinda sucks that you now have to type these things in 2 places. 
```python
# Python
brief_explanation_min = 10
brief_explanation_max = 1000
```
```javascript
// JS
const MIN_BRIEF_EXPLANATION = 10;
const MAX_BRIEF_EXPLANATION = 1000;
```

Let's hope we never forget :)




Hey Peter, I appreciate that you want to fix small things quickly, but I wish you'd have waited a bit. This is not a priority, and the report doesn't have all the details to design a solution. When you show people the minimum characters required, those who had put noise in there, will just generate more noise, but in this case it's not a big deal to lose some submissions, since those with less than 10 characters will in most cases not be actionable anyway.

My actual solution for now would have been to update the spreadsheet I dump this into to hide all submissions with less than 10 characters with a column filter.

Resolving patch:
diff --git a/kuma/api/v1/serializers.py b/kuma/api/v1/serializers.py
--- a/kuma/api/v1/serializers.py
+++ b/kuma/api/v1/serializers.py
@@ -9,7 +9,11 @@ class BCSignalSerializer(serializers.Serializer):
     browsers = serializers.CharField(max_length=255)
     slug = serializers.CharField(max_length=255)
     locale = serializers.CharField(max_length=7)
-    explanation = serializers.CharField(allow_blank=True, max_length=1000)
+    explanation = serializers.CharField(
+        # Make sure these match the constants in bcd-signal.jsx
+        max_length=1000,
+        min_length=10,
+    )
     supporting_material = serializers.CharField(
         allow_blank=True, required=False, max_length=1000
     )


Test patch:
diff --git a/kuma/api/v1/tests/test_views.py b/kuma/api/v1/tests/test_views.py
--- a/kuma/api/v1/tests/test_views.py
+++ b/kuma/api/v1/tests/test_views.py
@@ -6,6 +6,7 @@
 from kuma.core.tests import assert_no_cache_header
 from kuma.core.urlresolvers import reverse
 from kuma.search.tests import ElasticTestCase
+from kuma.wiki.models import BCSignal
 from kuma.wiki.templatetags.jinja_helpers import absolutify
 
 
@@ -384,3 +385,85 @@ def test_get_nonexisting_user(db, client, http_method):
     response = getattr(client, http_method)(url)
     assert response.status_code == 404
     assert_no_cache_header(response)
+
+
+@pytest.mark.django_db
+def test_bc_signal_happy_path(client, root_doc):
+    url = reverse("api.v1.bc_signal")
+    response = client.post(
+        url,
+        {
+            "slug": root_doc.slug,
+            "locale": root_doc.locale,
+            "explanation": "My explanation",
+            "feature": "Feet",
+            "browsers": "One,Two",
+            "supporting_material": "Extra things",
+        },
+    )
+    assert response.status_code == 201
+
+    bc_signal = BCSignal.objects.all().first()
+    assert bc_signal.document == root_doc
+    assert bc_signal.browsers == "One,Two"
+    assert bc_signal.explanation == "My explanation"
+    assert bc_signal.feature == "Feet"
+    assert bc_signal.supporting_material == "Extra things"
+
+
+@pytest.mark.django_db
+def test_bc_signal_disabled(client, settings):
+    settings.ENABLE_BCD_SIGNAL = False
+    url = reverse("api.v1.bc_signal")
+    response = client.post(url)
+    assert response.status_code == 400
+
+
+@pytest.mark.parametrize(
+    "explanation", ("", "tooshort", "x" * 1001), ids=("empty", "too-short", "too-long")
+)
+@pytest.mark.django_db
+def test_bc_signal_explanation_validation(client, root_doc, explanation):
+    url = reverse("api.v1.bc_signal")
+    data = {
+        "slug": root_doc.slug,
+        "locale": root_doc.locale,
+        "explanation": explanation,
+        "feature": "Feet",
+        "browsers": "One,Two",
+        "supporting_material": "Extra things",
+    }
+    response = client.post(url, data)
+    assert response.status_code == 400
+    assert response.json()["explanation"]
+
+
+@pytest.mark.django_db
+def test_bc_signal_document_validation(client):
+    url = reverse("api.v1.bc_signal")
+    response = client.post(
+        url,
+        {
+            "slug": "xxx",
+            "locale": "yyy",
+            "explanation": "My explanation",
+            "feature": "Feet",
+            "browsers": "One,Two",
+            "supporting_material": "Extra things",
+        },
+    )
+    assert response.status_code == 400
+    assert response.json() == ["Document not found"]
+
+
+@pytest.mark.django_db
+def test_bc_signal_http_method(client):
+    url = reverse("api.v1.bc_signal")
+    response = client.get(url)
+    assert response.status_code == 405
+
+    response = client.head(url)
+    assert response.status_code == 405
+
+    response = client.put(url)
+    assert response.status_code == 405
diff --git a/kuma/settings/testing.py b/kuma/settings/testing.py
--- a/kuma/settings/testing.py
+++ b/kuma/settings/testing.py
@@ -88,6 +88,9 @@
 # Because that's what all the tests presume.
 SITE_ID = 1
 
+# Because it's on by default
+ENABLE_BCD_SIGNAL = True
+
 # Stripe API KEY settings
 STRIPE_PUBLIC_KEY = "wubba lubba dub duab"
 STRIPE_SECRET_KEY = "wubba lubba dub dub"