Skip to content

Commit 665f091

Browse files
maebealeclaude
andcommitted
Prune redundant slow system specs; convert non-JS ones to request specs
The system suite booted real headless Chrome for many specs that only rendered static pages, making the suite slow without adding coverage a faster layer couldn't provide. This moves that coverage down to request specs and removes dead test files that never ran. - Convert 9 browser-booting, non-JS system specs to request specs (faqs, sectors, login, home/community-news, story ideas, workshop variation ideas, workshop logs, organization sectors, stories). Each conversion preserves the original assertions — including link-vs-text checks via Capybara.string(response.body) — and adds coverage that was previously missing (sector filtering, workshop-log show, stories empty state and body search). - Delete 17 spec/system/*_test.rb files: RSpec only loads *_spec.rb, so these never executed. Their flows are either already covered by request specs or were dormant; removing them stops them masquerading as coverage. Remaining system specs all genuinely require a browser (Stimulus, TomSelect, Turbo streams, accept_confirm, file upload/download). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f81b949 commit 665f091

35 files changed

Lines changed: 326 additions & 2632 deletions

spec/requests/faqs_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
expect(response.body).to include("Unpublished FAQ")
7171
end
7272

73+
it "shows the New FAQ admin control" do
74+
get faqs_path
75+
expect(response.body).to include("New FAQ")
76+
end
77+
7378
it "filters by unpublished param" do
7479
get faqs_path, params: { published: "false" } # needs to be string param
7580
expect(response.body).to include("Unpublished FAQ")
@@ -109,6 +114,12 @@
109114
expect(response.body).to include("Published FAQ")
110115
expect(response.body).not_to include("Unpublished FAQ")
111116
end
117+
118+
it "hides admin controls" do
119+
get faqs_path
120+
expect(response.body).not_to include("New FAQ")
121+
expect(response.body).not_to include(edit_faq_path(published_faq))
122+
end
112123
end
113124

114125
context "as a guest" do
@@ -124,6 +135,12 @@
124135
expect(response.body).not_to include("Published FAQ")
125136
expect(response.body).not_to include("Unpublished FAQ")
126137
end
138+
139+
it "hides admin controls" do
140+
get faqs_path
141+
expect(response.body).not_to include("New FAQ")
142+
expect(response.body).not_to include(edit_faq_path(public_faq))
143+
end
127144
end
128145
end
129146

spec/requests/home_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@
2020
end
2121
end
2222

23+
describe "GET / (signed in)" do
24+
it "shows the Upcoming Events and Community News sections" do
25+
user = create(:user)
26+
create(:person, user: user)
27+
sign_in user
28+
29+
get root_path
30+
31+
expect(response).to have_http_status(:ok)
32+
expect(response.body).to include("Upcoming Events")
33+
expect(response.body).to include("Community News")
34+
end
35+
end
36+
2337
describe "GET /home/* (turbo frame lazy loading)" do
2438
%w[workshops resources events community_news stories video_recordings].each do |section|
2539
it "#{section} returns 200 for visitors" do

spec/requests/login_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
require "rails_helper"
2+
3+
RSpec.describe "User login", type: :request do
4+
let(:password) { "MyString" }
5+
let(:generic_error) { "Invalid email or password. Please email us or fill out our Contact Us form for assistance." }
6+
7+
def log_in(email, password)
8+
post user_session_path, params: { user: { email: email, password: password } }
9+
end
10+
11+
context "when user is inactive" do
12+
let(:user) { create(:user, password: password, inactive: true) }
13+
14+
it "does not allow login and shows the generic error" do
15+
log_in(user.email, password)
16+
17+
expect(response).to redirect_to(new_user_session_path)
18+
follow_redirect!
19+
expect(response.body).to include(generic_error)
20+
end
21+
end
22+
23+
context "when credentials are wrong" do
24+
let(:user) { create(:user, password: password) }
25+
26+
it "shows the generic error" do
27+
log_in(user.email, "wrong_password")
28+
29+
expect(response).to have_http_status(:unprocessable_content)
30+
expect(response.body).to include(generic_error)
31+
end
32+
end
33+
34+
context "when user is active and unlocked" do
35+
let(:user) { create(:user, password: password) }
36+
37+
it "logs in successfully" do
38+
log_in(user.email, password)
39+
40+
expect(response).to redirect_to(root_path)
41+
follow_redirect!
42+
expect(response.body).not_to include(generic_error)
43+
end
44+
end
45+
end

spec/requests/organizations_spec.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,57 @@
6767
end
6868
end
6969

70+
describe "sector displays" do
71+
let!(:organization_with_sectors) do
72+
affiliated_sector_1 = create(:sector, name: "Affiliated Sector 1")
73+
affiliated_sector_2 = create(:sector, name: "Affiliated Sector 2")
74+
person_1 = create(:person)
75+
person_2 = create(:person)
76+
create(:sectorable_item, sector: affiliated_sector_1, sectorable: person_1)
77+
create(:sectorable_item, sector: affiliated_sector_2, sectorable: person_2)
78+
79+
org = create(:organization, organization_status: organization_status)
80+
create(:sectorable_item, sector: create(:sector, name: "Direct Sector 1"), sectorable: org)
81+
create(:sectorable_item, sector: create(:sector, name: "Direct Sector 2"), sectorable: org)
82+
create(:affiliation, organization: org, person: person_1, position: :default)
83+
create(:affiliation, organization: org, person: person_2, position: :default)
84+
org
85+
end
86+
87+
it "truncates sectors to the first 3 with a 'more' indicator on the show page" do
88+
get organization_url(organization_with_sectors)
89+
90+
page = Capybara.string(response.body)
91+
all_sector_names = organization_with_sectors.all_sectors.map(&:name).sort
92+
expect(all_sector_names.length).to be > 3
93+
all_sector_names.first(3).each { |name| expect(page).to have_content(name) }
94+
expect(page).not_to have_content(all_sector_names[3])
95+
expect(page).to have_content(/\+?[0-9]+ more|\.\.\./i)
96+
end
97+
98+
it "lists all sectors with per-sector people counts on the populations served page" do
99+
get populations_served_organization_url(organization_with_sectors)
100+
101+
page = Capybara.string(response.body)
102+
expect(page).to have_content("Sector Distribution")
103+
expect(page).to have_content(organization_with_sectors.name)
104+
105+
organization_with_sectors.all_sectors.map(&:name).each do |name|
106+
expect(page).to have_content(name)
107+
end
108+
109+
people = organization_with_sectors.users.includes(person: :sectors).map(&:person).compact
110+
expected_counts = Hash.new(0)
111+
people.each do |person|
112+
primary_sector = person.sectors.first
113+
expected_counts[primary_sector.name] += 1 if primary_sector
114+
end
115+
expected_counts.each do |_sector_name, count|
116+
expect(page).to have_content("#{count} #{count == 1 ? 'person' : 'people'}")
117+
end
118+
end
119+
end
120+
70121
describe "GET /new" do
71122
it "renders a successful response" do
72123
get new_organization_url

spec/requests/sectors_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,44 @@
2727
get sectors_url
2828
expect(response).to be_successful
2929
end
30+
31+
context "filtering" do
32+
let!(:sectors) do
33+
[
34+
create(:sector, name: "Sector1", published: true),
35+
create(:sector, name: "Sector2", published: true),
36+
create(:sector, name: "Sector3", published: false),
37+
create(:sector, name: "Sector4", published: false)
38+
]
39+
end
40+
41+
it "returns all sectors without filters" do
42+
get sectors_url
43+
sectors.each { |sector| expect(response.body).to include(sector.name) }
44+
end
45+
46+
it "returns only published sectors when published=true" do
47+
get sectors_url, params: { published: "true" }
48+
expect(response.body).to include("Sector1", "Sector2")
49+
expect(response.body).not_to include("Sector3")
50+
expect(response.body).not_to include("Sector4")
51+
end
52+
53+
it "returns only unpublished sectors when published=false" do
54+
get sectors_url, params: { published: "false" }
55+
expect(response.body).to include("Sector3", "Sector4")
56+
expect(response.body).not_to include("Sector1")
57+
expect(response.body).not_to include("Sector2")
58+
end
59+
60+
it "filters by name" do
61+
get sectors_url, params: { sector_name: "Sector1" }
62+
expect(response.body).to include("Sector1")
63+
expect(response.body).not_to include("Sector2")
64+
expect(response.body).not_to include("Sector3")
65+
expect(response.body).not_to include("Sector4")
66+
end
67+
end
3068
end
3169

3270
describe "GET /show" do

spec/requests/stories_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@
6666
expect(response.body).not_to include(story_in_other_org.title)
6767
end
6868

69+
it "shows the empty-state message when no stories match" do
70+
get stories_url(title: "no-such-story-zzz"), headers: { "Turbo-Frame" => "story_results" }
71+
expect(response.body).to include("No stories found")
72+
end
73+
74+
it "filters by title and body query together" do
75+
match = Story.create!(base_attributes.merge(title: "Best Story Match", rhino_body: "healing through art", published: true))
76+
title_only = Story.create!(base_attributes.merge(title: "Best Story Other", rhino_body: "something else", published: true))
77+
body_only = Story.create!(base_attributes.merge(title: "Unrelated Tale", rhino_body: "healing through art", published: true))
78+
79+
get stories_url(title: "Best Story", query: "healing"), headers: { "Turbo-Frame" => "story_results" }
80+
81+
expect(response.body).to include(match.title)
82+
expect(response.body).not_to include(title_only.title)
83+
expect(response.body).not_to include(body_only.title)
84+
end
85+
6986
describe "external link handling" do
7087
let(:turbo_headers) { { "Turbo-Frame" => "story_results" } }
7188

spec/requests/story_ideas_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@
5959
get story_idea_url(create(:story_idea))
6060
expect(response).to be_successful
6161
end
62+
63+
it "renders the organization and creator as links" do
64+
creator = create(:user)
65+
creator_person = create(:person, user: creator)
66+
org = create(:organization, name: "Community Arts Project")
67+
story_idea = create(:story_idea, created_by: creator, organization: org)
68+
69+
get story_idea_url(story_idea)
70+
71+
page = Capybara.string(response.body)
72+
expect(page).to have_link(org.name, href: organization_path(org))
73+
expect(page).to have_link(creator.name, href: person_path(creator_person))
74+
end
6275
end
6376

6477
describe "GET /new" do
@@ -228,6 +241,29 @@
228241
get story_idea_url(story_idea)
229242
expect(response).to be_successful
230243
end
244+
245+
it "renders the organization as plain text and the creator as a link" do
246+
person = create(:person, user: regular_user)
247+
org = create(:organization, name: "Community Arts Project")
248+
story_idea = create(:story_idea, created_by: regular_user, organization: org)
249+
250+
get story_idea_url(story_idea)
251+
252+
page = Capybara.string(response.body)
253+
expect(page).to have_text(org.name)
254+
expect(page).not_to have_link(org.name)
255+
expect(page).to have_link(regular_user.name, href: person_path(person))
256+
end
257+
258+
it "renders the creator as plain text when they have no person record" do
259+
story_idea = create(:story_idea, created_by: regular_user)
260+
261+
get story_idea_url(story_idea)
262+
263+
page = Capybara.string(response.body)
264+
expect(page).to have_text(regular_user.name)
265+
expect(page).not_to have_link(regular_user.name)
266+
end
231267
end
232268
end
233269

spec/requests/workshop_logs_spec.rb

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,79 @@
5454
clear_enqueued_jobs
5555
end
5656

57+
describe "GET /show" do
58+
it "renders the organization as plain text and the creator as a link (non-admin)" do
59+
person = create(:person, user: user)
60+
create(:affiliation, person: person, organization: organization)
61+
workshop_log = create(:workshop_log, created_by: user, organization: organization,
62+
workshop: workshop, windows_type: windows_type, workshop_held_on: 1.day.ago)
63+
64+
get workshop_log_path(workshop_log)
65+
66+
page = Capybara.string(response.body)
67+
expect(page).to have_text(organization.name)
68+
expect(page).not_to have_link(organization.name)
69+
expect(page).to have_link(user.name, href: person_path(person))
70+
end
71+
72+
it "renders the creator as plain text when they have no person record" do
73+
workshop_log = create(:workshop_log, created_by: user, organization: organization,
74+
workshop: workshop, windows_type: windows_type, workshop_held_on: 1.day.ago)
75+
76+
get workshop_log_path(workshop_log)
77+
78+
page = Capybara.string(response.body)
79+
expect(page).to have_text(user.name)
80+
expect(page).not_to have_link(user.name)
81+
end
82+
83+
it "shows the external title in the heading and beside the Workshop label when there is no workshop" do
84+
workshop_log = create(:workshop_log, created_by: user, organization: organization,
85+
workshop: nil, windows_type: windows_type,
86+
external_workshop_title: "Community Mural Project", workshop_held_on: 1.day.ago)
87+
88+
get workshop_log_path(workshop_log)
89+
90+
page = Capybara.string(response.body)
91+
expect(page).to have_css("h1", text: "Community Mural Project")
92+
workshop_div = page.find("span", text: "Workshop:").ancestor("div", match: :first)
93+
expect(workshop_div).to have_text("Community Mural Project")
94+
end
95+
96+
it "shows the workshop name in the heading and both the workshop and external title when both are present" do
97+
titled_workshop = create(:workshop, :published, title: "Healing Through Art", windows_type: windows_type)
98+
workshop_log = create(:workshop_log, created_by: user, organization: organization,
99+
workshop: titled_workshop, windows_type: windows_type,
100+
external_workshop_title: "Guest-led Session", workshop_held_on: 1.day.ago)
101+
102+
get workshop_log_path(workshop_log)
103+
104+
page = Capybara.string(response.body)
105+
expect(page).to have_css("h1", text: "Healing Through Art")
106+
workshop_div = page.find("span", text: "Workshop:").ancestor("div", match: :first)
107+
expect(workshop_div).to have_text("Healing Through Art")
108+
expect(workshop_div).to have_text("Guest-led Session")
109+
end
110+
111+
context "as an admin" do
112+
let(:admin) { create(:user, :admin) }
113+
before { sign_in admin }
114+
115+
it "renders the organization and creator as links" do
116+
person = create(:person, user: user)
117+
create(:affiliation, person: person, organization: organization)
118+
workshop_log = create(:workshop_log, created_by: user, organization: organization,
119+
workshop: workshop, windows_type: windows_type, workshop_held_on: 1.day.ago)
120+
121+
get workshop_log_path(workshop_log)
122+
123+
page = Capybara.string(response.body)
124+
expect(page).to have_link(organization.name, href: organization_path(organization))
125+
expect(page).to have_link(user.name, href: person_path(person))
126+
end
127+
end
128+
end
129+
57130
describe "GET /index" do
58131
it "loads the index page successfully" do
59132
get workshop_logs_path

0 commit comments

Comments
 (0)