Some CONSUL installations might want to customize their URLs. For
instance, Spanish institutions might want to use "/propuestas" instead
of "/proposals". In that case, it would be impossible to add proposals
as related content because the related contents controller assumed the
name of the model was part of the URL.
Using `recognize_path` instead of manually analyzing the URL solves the
issue.
Now that we don't call the `constantize` method on an empty string as we
previously did, we can be more specific in the `rescue` block and point
out that the only exception we expect is the one where users enter a
route which isn't recognized.
Although it wasn't a real security concern because we were only calling
a `find_by` method based on the user input, it's a good practice to
avoid using constants based on user parameters.
Since we don't use the `find_by` method anymore but we still need to
check the associated record exists, we're changing the validations in
the `RelatedContent` model to do exactly that.
We were manually checking validation rules (like both relationable
objects are present, or they're both the same object) in the controller
and then using the `save!` method.
However, we usually use the `save` method (which checks all validations)
in a condition, and proceed depending on the result.
Now we're taking the same approach here. This means introducing a new
validation rule in the model to check whether both relationable objects
are the same, which is more robust than checking a condition in the
controller.
We were only showing these actions to users with small screens and to
mouse users on hover. Keyboard users or users with a touch screen of a
medium or large size could never find out the actions were there.
It didn't have a `for` attribute and so it wasn't correctly associated
with its input. That means clicking on / touching the label didn't have
the effect of focusing on the field, and screen readers wouldn't
announce the label.
The button was announced as expanded when the form was hidden and as
collapsed when the form was shown.
This is because Foundation sets the expanded attribute based on whether
the class to toggle already exists. Since initially the form had the
"hide" class and the button toggled that class, Foundation checked that
the class was already present and so set the button as expanded.
So we're changing the toggler class for a class we don't use at all,
just so Foundation initiall sets `aria-expanded=false` and then changes
it to `aria-expanded=true` after the button is clicked. Then we're
ignoring this class completely and are styling this form with CSS
instead.
We could also use a toggler class like "visible" and write something
like:
```
.add-related-content + form:not(.visible) {
display: none;
}
```
However, using ARIA attributes is more robust as it guarantees the
styles will always be in sync with what screen reader users experience.
And we could also remove all the Foundation toggler functionality and
use our own JavaScript to handle the button state. We might do so in the
future.
We're removing the parenthesis after page expectations (as we do almost
everywhere) and we're replacing `have_selector` with `have_css`, since
on that file we were using `have_css` everywhere except in one place.
Using placeholders having similar (or identical) text as already present
as a label has a few issues.
First, it's a distraction. Reading the same information twice is
useless, requires an extra effort, and might even frustrate users.
Second, if users start typing before reading the placeholder and see it
disappear, they might think they're missing relevant information,
delete what they typed, and read the placeholder. That will get them
nowhere.
Finally, we display placeholders using a text offering very low contrast
against the background, so users don't think the placeholder is an
actual value entered in the field. Using such low contrast makes the
text hard to read, particularly for users with visual impairments.
So we're removing these placeholders.
This commit only deals with placeholder texts with similar (or
identical) texts as the label text. There might be other places where we
should replace placeholder texts with labels, but that's a different
topic.
A button cannot be inside an anchor tag, and it might confuse some
browsers or screen readers.
We're also making it clear in the tests that the intention is to use a
button there by using `click_button` instead of `click_on` since the
latter also clicks on links.
This test will fail if the ID of the created record is the same as the
ID of the first user we create in the test. The chance is very low due
to the `rand(9999999)` which causes the test to fail just once every ten
million times. However, why assigning the ID in the first place? Without
it, the test will never fail due to conflicting IDs.
The test "updates officer_assignment_id_log if amount changes" failed
when the ID we assigned when creating the records was the same as the ID
of the first officer assignment created during that test. It's recently
happened while running our test suite [1] with the following error:
```
1) Poll::Recount logging changes updates officer_assignment_id_log if
amount changes
Failure/Error: poll_recount.officer_assignment =
create(:poll_officer_assignment, id: 101)
ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique
constraint "poll_officer_assignments_pkey"
DETAIL: Key (id)=(101) already exists.
```
We're also removing the IDs in the "updates officer_assignment_id_log if
amount changes" test to avoid any possible issues, even if in this test
I think no other officer assignments are created and so there can't be
another record with the same ID.
[1] https://github.com/consul/consul/runs/2818889296
When administrators disabled features and users tried to access them
with the browser, we were responding with a 500 "Internal Server Error"
page, which in my humble opinion was incorrect. There was no error at
all; the server worked exactly as expected.
I think a 403 "Forbidden" code is better; since that feature is
disabled, we're refusing to let users access it.
We could also respond with a 404 "Not found", although I wonder whether
that'll be confusing when administrators temporarily or accidentally
disable a feature.
A similar thing might happen if we responded with a 410 "Gone" code.
Actually this case might be more confusing since users aren't that
familiar with this code.
In any case, all these options are better than the 500 error.
Note we don't cast negative votes when users remove their support. That
way we provide compatibility for institutions who have implemented real
negative votes (in case there are / will be any), and we also keep the
database meaningful: it's not that users downvoted something; they
simply removed their upvote.
Co-Authored-By: Javi Martín <javim@elretirao.net>
Co-Authored-By: Julian Nicolas Herrero <microweb10@gmail.com>
Since we're going to add an action to remove supports, having a separate
controller makes things easier.
Note there was a strange piece of code which assumed users were not
verified if they couldn't vote investments. Now the code is also
strange, since it assumes users are not verified if they can't create
votes. We might need to revisit these conditions if our logic changes in
the future.
It was hard to notice what was going on when supporting one investment
which was at the bottom of the investment index page.
I wonder whether we should add the title of the investment to this text;
I'm not doing so because we don't do that anywhere else.
If I'm right, the `investment_votes` instance variable only exists to
avoid several database queries to get whether the current user has
supported each of the investments.
However, that doesn't make much sense when only one investment is shown.
In this case, the number of queries stays the same, and so we can
simplify the code by rendering the component with an optional parameter.
Using links combined with JavaScript to generate POST requests to the
browser has a few issues.
An obvious one is that it doesn't work for users without JavaScript
enabled (which lately I've noticed are more common than I thought, even
though I've been one of them for years). These users will reach a 404
page.
Since CONSUL isn't currently designed to work without JavaScript
enabled, let's ignore this one for now.
A not so obvious issue is screen reader users might expect the link to
take them somewhere instead of performing an action (in this case,
sending a form to the server).
There might be more issues I'm unaware of. Quoting DHH [1]:
"Turning ahrefs into POSTs is a bit of an anti-pattern, especially for
a11y reasons. Better to use button_to with a styling."
So we're using a button instead. This way we can also simplify the code
and make the button disabled for unidentified users, which automatically
makes it impossible to focus it using the keyboard.
A possible disadvantage of using `button_to` is it will create a form
tag which will be announced to screen readers as a form landmark. I've
tested it with my screen reader and everything worked fine for me, but
some screen reader users might interact with these forms in a different
way and find it confusing, particularly in the case where the button is
disabled.
With this change, we're only changing links for buttons in one place.
There are other places where we should do similar changes.
[1] See issue 33 in https://github.com/hotwired/turbo-rails/
We meant to check that the link to support a different investment didn't
have an alert dialog. However, we were testing by trying to support the
same investment twice, and here there was no link at all, so the
assertion "there's no link with a dialog" always passed even when the
application didn't behave properly.
Now we check there's a link there, and that link doesn't have an
associated alert dialog.
In regular system tests, we prefer testing against the text of the link
because it's easier to recognize which link we're talking about.
I was wondering whether we should remove the `title` attribute
completely and use just the `aria-label` attribute. Quoting The Paciello
Group blog [1]:
"If you want to hide content from mobile and tablet users as well as
assistive tech users and keyboard only users, use the title attribute."
However, there's a case where mouse users might find the title attribute
useful. When visiting an investment page, the connection between the
"Support" link and the investment is not as clear as it is in the
investments index page, so it might not be clear what you're supporting.
As mentioned, though, this information will only be relevant for mouse
users, who nowadays represent less than half of our users.
[1] https://www.tpgi.com/using-the-html-title-attribute-updated/
This way blind screen reader users will know which project they're
supporting. In a list of investments, context might not be clear when a
link saying "Support" or "Support this project" is announced, but a link
saying "Support Renovate sidewalks in Main Street" is less ambiguous.
When identified users accessed the investments page, we were using the
`aria-hidden` attribute to hide this link from screen readers since
unidentified users can't support investments.
However, the link was still focusable using keyboard navigation. This
resulted in screen reader users reaching the link and being able to
click it, but getting no feedback at all about where they were or what
they were clicking on.
This is why the fourth ARIA rule says: "Do not use role="presentation" or
aria-hidden="true" on a focusable element" [1].
So we're replacing the link with a non-interactive element instead, like
we do in other places like proposals.
The accessibility of this part of the page for unidentified users still
has some issues; here we're only improving it up to a certain point.
[1] https://www.w3.org/TR/using-aria/#4thrule
The `js-` prefix (which I admit I'm not fond of) is usually used to
indicate the class is used by JavaScript files, not for using it in test
files. And in all the other similar tests, we're using the `in-favor`
class instead of the `js-in-favor` class.
This reverts commit 83fe74d53.
When supporting an investment in the management section through the
investment view, we were accessing an action in the public investments
controller. This meant the manager was the one supporting the investment
(as they'd be the `current_user` in this controller) and not the managed
user.
In the case of groups with many headings, voting the first time requires
a confirmation and then a regular (non-AJAX) request takes place. In
this case, users were redirected to the public area instead of remaining
in the management area.
Using the proper URL to vote solves the problem.
Note there was a comment about one of these tests failing in Travis.
Most probably the test failed because there was no expectation after
clicking the link with the investment title, so the "Support" button
(which is also present in the index page) was clicked before the
investment view was loaded.
The test was failing sometimes because the conditions we were checking
were the same before and after clicking the "Pending" link. So there was
a race condition between the request generated by clicking the "Pending"
link and the order to click the "Confirm moderation" link. This
sometimes resulted in the "Confirm moderation" link not being correctly
clicked.
The key was declared twice and so the first one ("Support this project")
was overwritten.
We're grouping all keys related to the investment list together in order
to reduce the chances of this issue happening again (or, at least, in
this part of the code).
Note one of the tests dealing with random results is a bit flaky; since
it's a permutation selecting 7 objects out of 12, it will fail about
once every 4 million times. We think this is acceptable.
Co-Authored-By: Julian Nicolas Herrero <microweb10@gmail.com>
In the form of creating a new investment was hiding the name of the
group if it had only one heading, but could be confusing to users if
there are, for example, five different groups of one heading.
The solution:
- If the budget has one group and one heading, the heading selector is
hidden.
- If the budget has one group and more than one heading, the group name
is hidden.
- If the budget has more than one group, the group name appears
regardless of the number of headings.
Note we're using an extra `<span>` element but we could use a CSS grid
layout instead. We're not using it because browser compatibility is only
94.56% at the time of writing.
The next warning is flooding the tests log:
`Options passed to Node#set but the RackTest driver
doesn't support any - ignoring`
As the rack test driver does not support any option, we
get rid of all `default_set_options` before each system
spec that uses this driver.