mirror of
https://github.com/ClaperCo/Claper.git
synced 2026-05-18 05:05:39 +02:00
Fix form submissions losing values when field names contain spaces or non-word characters
This commit is contained in:
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,4 +1,14 @@
|
||||
### v.2.5.0
|
||||
## v.2.5.1
|
||||
|
||||
### Security
|
||||
|
||||
- Update JS dependencies with high CVE
|
||||
|
||||
### Fixes and improvements
|
||||
|
||||
- Fix form submissions losing values when field names contain spaces or non-word characters
|
||||
|
||||
## v.2.5.0
|
||||
|
||||
### Features
|
||||
|
||||
@@ -27,7 +37,7 @@
|
||||
- Fix manager and presenter views while presentation conversion has no slide count yet
|
||||
- Fix crash on event manager pages when an event has multiple activity leaders
|
||||
|
||||
### v.2.4.0
|
||||
## v.2.4.0
|
||||
|
||||
### ⚠️ Breaking changes
|
||||
|
||||
@@ -59,7 +69,7 @@
|
||||
- Fix italian translation (#179)
|
||||
- Fix random poll choices (#184)
|
||||
|
||||
### v.2.3.2
|
||||
## v.2.3.2
|
||||
|
||||
### Fixes and improvements
|
||||
|
||||
@@ -71,7 +81,7 @@
|
||||
- Fix event code length validation (min: 5, max: 10)
|
||||
- Fix presentation upload progress when editing an event
|
||||
|
||||
### v.2.3.1
|
||||
## v.2.3.1
|
||||
|
||||
### Fixes and improvements
|
||||
|
||||
@@ -82,7 +92,7 @@
|
||||
- Add option to force login to submit quizzes
|
||||
- Fix url with question mark being flagged as a question
|
||||
|
||||
### v.2.3.0
|
||||
## v.2.3.0
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ defmodule ClaperWeb.EventLive.FormComponent do
|
||||
form={f}
|
||||
labelClass="text-white"
|
||||
fieldClass="bg-gray-700 text-white"
|
||||
key={safe_field_atom(field.name)}
|
||||
key={field_key(field.name)}
|
||||
name={field.name}
|
||||
required={field.required}
|
||||
value={
|
||||
@@ -75,7 +75,7 @@ defmodule ClaperWeb.EventLive.FormComponent do
|
||||
form={f}
|
||||
labelClass="text-white"
|
||||
fieldClass="bg-gray-700 text-white"
|
||||
key={safe_field_atom(field.name)}
|
||||
key={field_key(field.name)}
|
||||
name={field.name}
|
||||
required={field.required}
|
||||
value={
|
||||
@@ -176,13 +176,12 @@ defmodule ClaperWeb.EventLive.FormComponent do
|
||||
end
|
||||
end
|
||||
|
||||
defp safe_field_atom(name) when is_binary(name) do
|
||||
String.to_existing_atom(name)
|
||||
rescue
|
||||
ArgumentError ->
|
||||
if Regex.match?(~r/^[\w]{1,100}$/, name),
|
||||
do: String.to_atom(name),
|
||||
else: :invalid_field
|
||||
defp field_key(name) when is_binary(name) do
|
||||
try do
|
||||
String.to_existing_atom(name)
|
||||
rescue
|
||||
ArgumentError -> String.to_atom(name)
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_form(js \\ %JS{}) do
|
||||
|
||||
62
test/claper_web/controllers/stat_controller_test.exs
Normal file
62
test/claper_web/controllers/stat_controller_test.exs
Normal file
@@ -0,0 +1,62 @@
|
||||
defmodule ClaperWeb.StatControllerTest do
|
||||
use ClaperWeb.ConnCase, async: true
|
||||
|
||||
import Claper.{AccountsFixtures, FormsFixtures, PresentationsFixtures}
|
||||
|
||||
describe "POST /export/forms/:form_id" do
|
||||
setup %{conn: conn} do
|
||||
owner = confirmed_user_fixture()
|
||||
presentation_file = presentation_file_fixture(%{user: owner}, [:event])
|
||||
|
||||
form =
|
||||
form_fixture(%{
|
||||
presentation_file_id: presentation_file.id,
|
||||
title: "My Form",
|
||||
fields: [
|
||||
%{name: "First Name", type: "text", required: false},
|
||||
%{name: "Email Address", type: "email", required: false}
|
||||
]
|
||||
})
|
||||
|
||||
%{conn: log_in_user(conn, owner), form: form, event: presentation_file.event}
|
||||
end
|
||||
|
||||
test "exports CSV with field names containing spaces as headers and values", %{
|
||||
conn: conn,
|
||||
form: form,
|
||||
event: event
|
||||
} do
|
||||
{:ok, _submit} =
|
||||
Claper.Forms.create_or_update_form_submit(event.uuid, %{
|
||||
"attendee_identifier" => "attendee-1",
|
||||
"form_id" => form.id,
|
||||
"response" => %{"First Name" => "Ada", "Email Address" => "ada@example.com"}
|
||||
})
|
||||
|
||||
conn = post(conn, ~p"/export/forms/#{form.id}")
|
||||
|
||||
assert response_content_type(conn, :csv)
|
||||
body = response(conn, 200)
|
||||
|
||||
[header_line, data_line | _] = String.split(body, "\r\n", trim: true)
|
||||
|
||||
assert header_line =~ "First Name"
|
||||
assert header_line =~ "Email Address"
|
||||
|
||||
# The CSV row must contain the values, proving header→response key
|
||||
# alignment works for space-containing field names. Regression: when
|
||||
# submissions were saved under a wrong key, the CSV row was empty.
|
||||
assert data_line =~ "Ada"
|
||||
assert data_line =~ "ada@example.com"
|
||||
end
|
||||
|
||||
test "returns 403 for users who don't own the event", %{form: form} do
|
||||
stranger = confirmed_user_fixture()
|
||||
conn = build_conn() |> log_in_user(stranger)
|
||||
|
||||
conn = post(conn, ~p"/export/forms/#{form.id}")
|
||||
|
||||
assert response(conn, 403) == "Forbidden"
|
||||
end
|
||||
end
|
||||
end
|
||||
72
test/claper_web/live/event_live/form_component_test.exs
Normal file
72
test/claper_web/live/event_live/form_component_test.exs
Normal file
@@ -0,0 +1,72 @@
|
||||
defmodule ClaperWeb.EventLive.FormComponentTest do
|
||||
use ClaperWeb.ConnCase, async: true
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
import Claper.{AccountsFixtures, FormsFixtures, PresentationsFixtures}
|
||||
|
||||
alias ClaperWeb.EventLive.FormComponent
|
||||
|
||||
defp setup_form(field_attrs) do
|
||||
presentation_file = presentation_file_fixture(%{}, [:event])
|
||||
|
||||
form =
|
||||
form_fixture(%{
|
||||
presentation_file_id: presentation_file.id,
|
||||
fields: field_attrs
|
||||
})
|
||||
|
||||
%{event: presentation_file.event, form: form}
|
||||
end
|
||||
|
||||
describe "rendering fields with non-alphanumeric names" do
|
||||
test "renders an input whose name uses the original field name (with spaces)" do
|
||||
%{event: event, form: form} =
|
||||
setup_form([
|
||||
%{name: "First Name", type: "text", required: false},
|
||||
%{name: "Email Address", type: "email", required: false}
|
||||
])
|
||||
|
||||
html =
|
||||
render_component(FormComponent,
|
||||
id: "form-component",
|
||||
form: form,
|
||||
current_user: user_fixture(),
|
||||
attendee_identifier: nil,
|
||||
event: event,
|
||||
current_form_submit: nil
|
||||
)
|
||||
|
||||
# The HTML name attribute must contain the actual field name —
|
||||
# regression: a previous version mapped any non-\w name to :invalid_field,
|
||||
# so all space-containing fields collided on `form_submit[invalid_field]`
|
||||
# and submissions were saved under the wrong key.
|
||||
assert html =~ ~s(name="form_submit[First Name]")
|
||||
assert html =~ ~s(name="form_submit[Email Address]")
|
||||
refute html =~ "invalid_field"
|
||||
end
|
||||
|
||||
test "preserves submitted values when re-rendering for fields with spaces" do
|
||||
%{event: event, form: form} =
|
||||
setup_form([
|
||||
%{name: "First Name", type: "text", required: false}
|
||||
])
|
||||
|
||||
form_submit = %Claper.Forms.FormSubmit{
|
||||
response: %{"First Name" => "Ada"}
|
||||
}
|
||||
|
||||
html =
|
||||
render_component(FormComponent,
|
||||
id: "form-component",
|
||||
form: form,
|
||||
current_user: user_fixture(),
|
||||
attendee_identifier: nil,
|
||||
event: event,
|
||||
current_form_submit: form_submit
|
||||
)
|
||||
|
||||
assert html =~ ~s(value="Ada")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user