Links acting like buttons have a few disadvantages.
First, screen readers will announce them as "links". Screen reader users
usually associate links with "things that get you somewhere" and buttons
with "things that perform an action". So when something like "Delete,
link" is announced, they'll probably think this is a link which will
take them to another page where they can delete a record.
Furthermore, the URL of the link for the "destroy" action might be the
same as the URL for the "show" action (only one is accessed with a
DELETE request and the other one with a GET request). That means screen
readers could announce the link like "Delete, visited link", which is
very confusing.
They also won't work when opening links in a new tab, since opening
links in a new tab always results in a GET request to the URL the link
points to.
Finally, submit buttons work without JavaScript enabled, so they'll work
even if the JavaScript in the page hasn't loaded (for whatever reason).
For all these reasons (and probably many more), using a button to send
forms is IMHO superior to using links.
There's one disadvantage, though. Using `button_to` we create a <form>
tag, which means we'll generate invalid HTML if the table is inside
another form. If we run into this issue, we need to use `button_tag`
with a `form` attribute and then generate a form somewhere else inside
the HTML (with `content_for`).
Note we're using `button_to` with a block so it generates a <button>
tag. Using it in a different way the text would result in an <input />
tag, and input elements can't have pseudocontent added via CSS.
The following code could be a starting point to use the `button_tag`
with a `form` attribute. One advantage of this approach is screen
readers wouldn't announce "leaving form" while navigating through these
buttons. However, it doesn't work in Internet Explorer.
```
ERB:
<% content_for(:hidden_content, form_tag(path, form_options) {}) %>
<%= button_tag text, button_options %>
Ruby:
def form_id
path.gsub("/", "_")
end
def form_options
{ id: form_id, method: options[:method] }
end
def button_options
html_options.except(:method).merge(form: form_id)
end
Layout:
<%= content_for :hidden_content %> # Right before the `</body>`
```
378 lines
12 KiB
Ruby
378 lines
12 KiB
Ruby
require "rails_helper"
|
|
|
|
describe "Admin budgets", :admin do
|
|
it_behaves_like "nested imageable",
|
|
"budget",
|
|
"new_admin_budgets_wizard_budget_path",
|
|
{},
|
|
"imageable_fill_new_valid_budget",
|
|
"Continue to groups",
|
|
"New participatory budget created successfully!"
|
|
|
|
context "Load" do
|
|
before { create(:budget, slug: "budget_slug") }
|
|
|
|
scenario "finds budget by slug" do
|
|
visit edit_admin_budget_path("budget_slug")
|
|
|
|
expect(page).to have_content("Edit Participatory budget")
|
|
end
|
|
end
|
|
|
|
context "Index" do
|
|
scenario "Displaying no open budgets text" do
|
|
visit admin_budgets_path
|
|
|
|
expect(page).to have_content("There are no budgets.")
|
|
end
|
|
|
|
scenario "Displaying budgets" do
|
|
budget = create(:budget, :accepting)
|
|
visit admin_budgets_path
|
|
|
|
within "tr", text: budget.name do
|
|
expect(page).to have_content "Accepting projects"
|
|
expect(page).to have_content "Pending: No headings yet"
|
|
end
|
|
end
|
|
|
|
scenario "Filters by phase" do
|
|
create(:budget, :drafting, name: "Unpublished budget")
|
|
create(:budget, :accepting, name: "Accepting budget")
|
|
create(:budget, :selecting, name: "Selecting budget")
|
|
create(:budget, :balloting, name: "Balloting budget")
|
|
create(:budget, :finished, name: "Finished budget")
|
|
|
|
visit admin_budgets_path
|
|
|
|
expect(page).to have_content "Accepting budget"
|
|
expect(page).to have_content "Selecting budget"
|
|
expect(page).to have_content "Balloting budget"
|
|
|
|
within "tr", text: "Unpublished budget" do
|
|
expect(page).to have_content "Draft"
|
|
end
|
|
|
|
within "tr", text: "Finished budget" do
|
|
expect(page).to have_content "COMPLETED"
|
|
end
|
|
|
|
click_link "Finished"
|
|
|
|
expect(page).not_to have_content "Unpublished budget"
|
|
expect(page).not_to have_content "Accepting budget"
|
|
expect(page).not_to have_content "Selecting budget"
|
|
expect(page).not_to have_content "Balloting budget"
|
|
expect(page).to have_content "Finished budget"
|
|
|
|
click_link "Open"
|
|
|
|
expect(page).to have_content "Unpublished budget"
|
|
expect(page).to have_content "Accepting budget"
|
|
expect(page).to have_content "Selecting budget"
|
|
expect(page).to have_content "Balloting budget"
|
|
expect(page).not_to have_content "Finished budget"
|
|
end
|
|
|
|
scenario "Filters are properly highlighted" do
|
|
filters_links = { "all" => "All", "open" => "Open", "finished" => "Finished" }
|
|
|
|
visit admin_budgets_path
|
|
|
|
expect(page).not_to have_link(filters_links.values.first)
|
|
filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) }
|
|
|
|
filters_links.each do |current_filter, link|
|
|
visit admin_budgets_path(filter: current_filter)
|
|
|
|
expect(page).not_to have_link(link)
|
|
|
|
(filters_links.keys - [current_filter]).each do |filter|
|
|
expect(page).to have_link(filters_links[filter])
|
|
end
|
|
end
|
|
end
|
|
|
|
scenario "Delete budget from index" do
|
|
create(:budget, name: "To be deleted")
|
|
|
|
visit admin_budgets_path
|
|
|
|
within "tr", text: "To be deleted" do
|
|
message = "Are you sure? This action will delete \"To be deleted\" and can't be undone."
|
|
|
|
accept_confirm(message) { click_button "Delete" }
|
|
end
|
|
|
|
expect(page).to have_content("Budget deleted successfully")
|
|
expect(page).to have_content("There are no budgets.")
|
|
expect(page).not_to have_content "To be deleted"
|
|
end
|
|
end
|
|
|
|
context "Publish" do
|
|
let(:budget) { create(:budget, :drafting) }
|
|
|
|
scenario "Can preview budget before it is published" do
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
within_window(window_opened_by { click_link "Preview" }) do
|
|
expect(page).to have_current_path budget_path(budget)
|
|
end
|
|
end
|
|
|
|
scenario "Can preview a budget after it is published" do
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
accept_confirm { click_link "Publish budget" }
|
|
|
|
expect(page).to have_content "Participatory budget published successfully"
|
|
expect(page).not_to have_content "This participatory budget is in draft mode"
|
|
expect(page).not_to have_link "Publish budget"
|
|
|
|
within_window(window_opened_by { click_link "Preview" }) do
|
|
expect(page).to have_current_path budget_path(budget)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "Destroy" do
|
|
let!(:budget) { create(:budget) }
|
|
let(:heading) { create(:budget_heading, budget: budget) }
|
|
|
|
scenario "Destroy a budget without investments" do
|
|
visit edit_admin_budget_path(budget)
|
|
click_link "Delete budget"
|
|
|
|
expect(page).to have_content("Budget deleted successfully")
|
|
expect(page).to have_content("There are no budgets.")
|
|
end
|
|
|
|
scenario "Destroy a budget without investments but with administrators and valuators" do
|
|
budget.administrators << Administrator.first
|
|
budget.valuators << create(:valuator)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
click_link "Delete budget"
|
|
|
|
expect(page).to have_content "Budget deleted successfully"
|
|
expect(page).to have_content "There are no budgets."
|
|
end
|
|
|
|
scenario "Try to destroy a budget with investments" do
|
|
create(:budget_investment, heading: heading)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
click_link "Delete budget"
|
|
|
|
expect(page).to have_content("You cannot delete a budget that has associated investments")
|
|
expect(page).to have_content("There is 1 budget")
|
|
end
|
|
|
|
scenario "Try to destroy a budget with polls" do
|
|
create(:poll, budget: budget)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
click_link "Delete budget"
|
|
|
|
expect(page).to have_content("You cannot delete a budget that has an associated poll")
|
|
expect(page).to have_content("There is 1 budget")
|
|
end
|
|
end
|
|
|
|
context "Edit" do
|
|
let(:budget) { create(:budget) }
|
|
|
|
scenario "Show phases table" do
|
|
travel_to(Date.new(2015, 7, 15)) do
|
|
budget.update!(phase: "selecting")
|
|
budget.phases.valuating.update!(enabled: false)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
expect(page).to have_select "Active phase", selected: "Selecting projects"
|
|
|
|
expect(page).to have_table "Phases", with_cols: [
|
|
[
|
|
"Information",
|
|
"Accepting projects",
|
|
"Reviewing projects",
|
|
"Selecting projects Active",
|
|
"Valuating projects",
|
|
"Publishing projects prices",
|
|
"Voting projects",
|
|
"Reviewing voting"
|
|
],
|
|
[
|
|
"2015-07-15 00:00 - 2015-08-14 23:59",
|
|
"2015-08-15 00:00 - 2015-09-14 23:59",
|
|
"2015-09-15 00:00 - 2015-10-14 23:59",
|
|
"2015-10-15 00:00 - 2015-11-14 23:59",
|
|
"2015-11-15 00:00 - 2015-12-14 23:59",
|
|
"2015-11-15 00:00 - 2016-01-14 23:59",
|
|
"2016-01-15 00:00 - 2016-02-14 23:59",
|
|
"2016-02-15 00:00 - 2016-03-14 23:59"
|
|
],
|
|
[
|
|
"Yes",
|
|
"Yes",
|
|
"Yes",
|
|
"Yes",
|
|
"No",
|
|
"Yes",
|
|
"Yes",
|
|
"Yes"
|
|
]
|
|
]
|
|
|
|
within_table "Phases" do
|
|
within "tr", text: "Information" do
|
|
expect(page).to have_link "Edit"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
scenario "Show results and stats settings" do
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
within_fieldset "Show results and stats" do
|
|
expect(page).to have_field "Show results"
|
|
expect(page).to have_field "Show stats"
|
|
expect(page).to have_field "Show advanced stats"
|
|
end
|
|
end
|
|
|
|
scenario "Show CTA link in public site if added" do
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
expect(page).to have_content("Main call to action (optional)")
|
|
|
|
fill_in "Text on the link", with: "Participate now"
|
|
fill_in "The link takes you to (add a link)", with: "https://consulproject.org"
|
|
click_button "Update Budget"
|
|
|
|
expect(page).to have_content "Participatory budget updated successfully"
|
|
|
|
visit budgets_path
|
|
expect(page).to have_link("Participate now", href: "https://consulproject.org")
|
|
end
|
|
|
|
scenario "Changing name for current locale will update the slug if budget is in draft phase" do
|
|
budget.update!(published: false, name: "Old English Name")
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
select "Español", from: :add_language
|
|
fill_in "Name", with: "Spanish name"
|
|
click_button "Update Budget"
|
|
|
|
expect(page).to have_content "Participatory budget updated successfully"
|
|
|
|
visit budget_path(id: "old-english-name")
|
|
|
|
expect(page).to have_content "Old English Name"
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
select "English", from: :select_language
|
|
fill_in "Name", with: "New English Name"
|
|
click_button "Update Budget"
|
|
|
|
expect(page).to have_content "Participatory budget updated successfully"
|
|
|
|
visit budget_path(id: "new-english-name")
|
|
|
|
expect(page).to have_content "New English Name"
|
|
end
|
|
end
|
|
|
|
context "Update" do
|
|
scenario "Update budget" do
|
|
visit edit_admin_budget_path(create(:budget))
|
|
|
|
fill_in "Name", with: "More trees on the streets"
|
|
click_button "Update Budget"
|
|
|
|
expect(page).to have_content("More trees on the streets")
|
|
expect(page).to have_current_path(admin_budgets_path)
|
|
end
|
|
|
|
scenario "Deselect all selected staff" do
|
|
admin = Administrator.first
|
|
valuator = create(:valuator)
|
|
|
|
budget = create(:budget, administrators: [admin], valuators: [valuator])
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
click_link "1 administrator selected"
|
|
uncheck admin.name
|
|
|
|
expect(page).to have_link "Select administrators"
|
|
|
|
click_link "1 valuator selected"
|
|
uncheck valuator.name
|
|
|
|
expect(page).to have_link "Select valuators"
|
|
|
|
click_button "Update Budget"
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
expect(page).to have_link "Select administrators"
|
|
expect(page).to have_link "Select valuators"
|
|
end
|
|
end
|
|
|
|
context "Calculate Budget's Winner Investments" do
|
|
scenario "For a Budget in reviewing balloting" do
|
|
budget = create(:budget, :reviewing_ballots)
|
|
heading = create(:budget_heading, budget: budget, price: 4)
|
|
unselected = create(:budget_investment, :unselected, heading: heading, price: 1,
|
|
ballot_lines_count: 3)
|
|
winner = create(:budget_investment, :selected, heading: heading, price: 3,
|
|
ballot_lines_count: 2)
|
|
selected = create(:budget_investment, :selected, heading: heading, price: 2, ballot_lines_count: 1)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
expect(page).not_to have_content "See results"
|
|
click_link "Calculate Winner Investments"
|
|
expect(page).to have_content "Winners being calculated, it may take a minute."
|
|
expect(page).to have_content winner.title
|
|
expect(page).not_to have_content unselected.title
|
|
expect(page).not_to have_content selected.title
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
expect(page).to have_content "See results"
|
|
end
|
|
|
|
scenario "For a finished Budget" do
|
|
budget = create(:budget, :finished)
|
|
allow_any_instance_of(Budget).to receive(:has_winning_investments?).and_return(true)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
expect(page).to have_content "Calculate Winner Investments"
|
|
expect(page).to have_content "See results"
|
|
end
|
|
|
|
scenario "Recalculate for a finished Budget" do
|
|
budget = create(:budget, :finished)
|
|
create(:budget_investment, :winner, budget: budget)
|
|
|
|
visit edit_admin_budget_path(budget)
|
|
|
|
expect(page).to have_content "Recalculate Winner Investments"
|
|
expect(page).to have_content "See results"
|
|
expect(page).not_to have_content "Calculate Winner Investments"
|
|
|
|
visit admin_budget_budget_investments_path(budget)
|
|
click_link "Advanced filters"
|
|
check "Winners"
|
|
click_button "Filter"
|
|
|
|
expect(page).to have_content "Recalculate Winner Investments"
|
|
expect(page).not_to have_content "Calculate Winner Investments"
|
|
end
|
|
end
|
|
end
|