The `reload` method added to max_votes validation is needed because the
author gets here with some changes because of the around_action
`switch_locale`, which adds some changes to the current user record and
therefore, the lock method raises an exception when trying to lock it
requiring us to save or discard those record changes.
In case we receive consecutive requests we are locking the poll author record
until the first request transaction ends, so the author answers count during
subsequent requests validations is up to date.
By using the Rails `button_to` helper (which generates a form), and adapting the
response to `html` and `js` formats, the feature works with or without javascript
enabled.
We were rendering the `new` action, but that action doesn't exist.
Before commit ec861ca8e, we were rendering the `edit` action of an
answer, which was confusing as well.
Note that, when adding an invalid document, `@answer.documents` contains
that invalid document (which is not present in the database). Since
we're rendering the index, this new document would appear in the list of
the documents that can be deleted; to avoid that, we're kind of
"reloading" the answer object in the component by finding the record in
the database. We aren't using `@answer.reload` because doing so would
remove the validation errors.
We were using the same logic in four different places, so we're creating
a new class to handle that logic.
Note that I didn't find a way to delegate the `content` method to a
`Admin::TableActionsComponent`, so we're delegating the `action` method
instead. That means we need to create a method returning an
`Admin::TableActionsComponent`. We also need to cache this object;
otherwise we were getting an error when calling `actions.action` from
the `Admin::Poll::Questions::TableActionsComponent`.
Note that the `create` action doesn't create an image but updates an
answer instead. We're removing the references to `:create` in the
abilities since it isn't used.
In the future we might change the form to add an image to an answer
because it's been broken for ages since it shows all the attached
images.
Adding, modifiying, and/or deleting questions for an already started
poll is far away from being democratic and can lead to unwanted side
effects like missing votes in the results or stats.
So, from now on, only modifiying questions will be possible only if
the poll has not started yet.
In this form, the only case where `poll` might be present without
`question.poll` being present to is going to be the `new` action. We can
assign the poll in the `new` action and get rid of the `poll` variable
in the form.
We need to update a couple of tests because a poll is created in the
tests with a timestamp that includes nanoseconds and in the form to edit
the time of the poll the nanoseconds are not sent, meaning it was
detected as a change.
Instead of having to add `beginning_of_minute` to deal with an issue
with Capybara filling datetime fields as mentioned in commit 5a0fde4048,
we can travel to the beginning of the minute so we don't have to take
the seconds into account.
The extra check to see the voter count has increased was redundant; we
already check the request has finished inside the
`vote_for_poll_via_web` method and we check all three voters are created
in the results table.
Updating the poll so it's in the past after starting the browser might
result in database inconsistencies while running the tests, so we're
using `travel_to` instead.
We were already saving it as a time, but we didn't offer an interface to
select the time due to lack of decent browser support for this field
back when this feature was added.
However, nowadays all major browsers support this field type and, at the
time of writing, at least 86.5% of the browsers support it [1]. This
percentage could be much higher, since support in 11.25% of the browsers
is unknown.
Note we still need to support the case where this field isn't supported,
and so we offer a fallback and on the server side we don't assume we're
always getting a time. We're doing a strange hack so we set the field
type to text before changing its value; otherwise old Firefox browsers
crashed.
Also note that, until now, we were storing end dates in the database as
a date with 00:00 as its time, but we were considering the poll to be
open until 23:59 that day. So, in order to keep backwards compatibility,
we're adding a task to update the dates of existing polls so we get the
same behavior we had until now.
This also means budget polls are now created so they end at the
beginning of the day when the balloting phase ends. This is consistent
with the dates we display in the budget phases table.
Finally, there's one test where we're using `beginning_of_minute` when
creating a poll. That's because Chrome provides an interface to enter a
time in a `%H:%M` format when the "seconds" value of the provided time
is zero. However, when the "seconds" value isn't zero, Chrome provides
an interface to enter a time in a `%H:%M:%S` format. Since Capybara
doesn't enter the seconds when using `fill_in` with a time, the test
failed when Capybara tried to enter a time in the `%H:%M` format when
Chrome expected a time in the `%H:%M:%S` format.
To solve this last point, an alternative would be to manually provide
the format when using `fill_in` so it includes the seconds.
[1] https://caniuse.com/mdn-html_elements_input_type_datetime-local
This was the only place where we used the `? <= field` format; we use
the `field >= ?` format everywhere else.
We're also adding a variable to make it easier to understand that the
value is the same in both cases and to make the line shorter ;).