diff --git a/public/.well-known/appspecific/com.chrome.devtools.json b/public/.well-known/appspecific/com.chrome.devtools.json
new file mode 100644
index 000000000..e69de29bb
diff --git a/spec/features/admin_user_spec.rb b/spec/features/admin_user_spec.rb
index 4ec647e89..790bb644d 100644
--- a/spec/features/admin_user_spec.rb
+++ b/spec/features/admin_user_spec.rb
@@ -126,8 +126,8 @@
visit conservation_records_path
click_link(conservation_record.title, match: :prefer_exact)
- expect(page).to have_button('Add In-House Repairs')
- click_button('Add In-House Repairs')
+ expect(page).to have_button('Add In-House Repair')
+ click_button('Add In-House Repair')
select('Haritha Vytla', from: 'in_house_performed_by_user_id', match: :first)
select('Mend paper', from: 'in_house_repair_type', match: :first)
fill_in 'in_house_minutes_spent', with: '10'
diff --git a/spec/features/end_to_end_spec.rb b/spec/features/end_to_end_spec.rb
index 02f00b404..0241359b8 100644
--- a/spec/features/end_to_end_spec.rb
+++ b/spec/features/end_to_end_spec.rb
@@ -42,7 +42,7 @@
expect(page).to have_content('Cost and Return Information')
expect(page).to have_button('Save Cost and Return Information', disabled: true)
expect(page).to have_no_link('Edit Conservation Record')
- expect(page).to have_no_button('Add In-House Repairs')
+ expect(page).to have_no_button('Add In-House Repair')
expect(page).to have_no_button('Add External Repair')
expect(page).to have_no_button('Add Conservators and Technicians')
expect(page).to have_button('Save Treatment Report', disabled: true)
@@ -106,8 +106,8 @@
# In_House Repair
visit conservation_records_path
click_link(conservation_record.title, match: :prefer_exact)
- expect(page).to have_button('Add In-House Repairs')
- click_button('Add In-House Repairs')
+ expect(page).to have_button('Add In-House Repair')
+ click_button('Add In-House Repair')
expect(page).to have_button('Create In-House Repair Record')
select('Chuck Greenman', from: 'in_house_performed_by_user_id', match: :first)
select('Soft slipcase', from: 'in_house_repair_type', match: :first)
@@ -210,7 +210,7 @@
let!(:staff_code) { create(:staff_code, code: 'test', points: 10) }
let(:today_date) { Time.zone.today.strftime('%Y-%m-%d') }
- it 'allows User to login and show Conservation Records' do
+ it 'allows User to login and show Conservation Records', aggregate_failures: true do
# Login
visit dev_login_path
expect(page).to have_text('Please sign in with your UC', wait: 10)
@@ -309,8 +309,8 @@
# Create In-House Repair
visit conservation_records_path
find('a', text: conservation_record.title, match: :prefer_exact, wait: 10).click
- expect(page).to have_button('Add In-House Repairs', wait: 10)
- find('button', text: 'Add In-House Repairs', wait: 10).click
+ expect(page).to have_button('Add In-House Repair', wait: 10)
+ find('button', text: 'Add In-House Repair', wait: 10).click
select 'Haritha Vytla', from: 'in_house_performed_by_user_id', match: :first
repair_types = find('#in_house_repair_type', wait: 10).all('option').collect(&:text)
expect(repair_types[1..]).to start_with('updated_key_string')
@@ -322,18 +322,27 @@
expect(page).to have_text('Mend paper performed by Haritha Vytla in 2 minutes. Other note: Some Other note for the in-house repair', wait: 10)
# Delete In-House Repair
- expect(page).to have_selector("a[id='delete_in_house_repair_record_1']", visible: true, wait: 10)
- find("a[id='delete_in_house_repair_record_1']", visible: true, wait: 10)
+ repair = InHouseRepairRecord.last
+ record_id = page.current_path.split('/')[2]
+ delete_path = "/conservation_records/#{record_id}/in_house_repair_records/#{repair.id}"
+ selector = "a[data-method='delete'][href='#{delete_path}']"
+
+ # wait for the link to appear
+ expect(page).to have_selector(selector, visible: true, wait: 10)
+
+ # click it under the confirm dialog, retrying if needed
retries = 3
begin
accept_confirm do
- find("a[id='delete_in_house_repair_record_1']", wait: 10).click
+ find(selector, visible: true, wait: 10).click
end
rescue Capybara::ModalNotFound
retries -= 1
retry if retries.positive?
raise
end
+
+ # verify the record has been removed from the page
expect(page).to have_no_text('Mend paper performed by Haritha Vytla', wait: 10)
# Create External Repair
@@ -358,12 +367,30 @@
expect(page).to have_text('Wash performed by Amanda Buck. Other note: Some Other note for the external repair', wait: 10)
# Delete External Repair
- expect(page).to have_selector("a[id='delete_external_repair_record_1']", wait: 10)
- accept_confirm(wait: 15) do
- find("a[id='delete_external_repair_record_1']", wait: 10).click
+ repair = ExternalRepairRecord.last
+ record_id = page.current_path.split('/')[2] # grabs the :conservation_record id from “/conservation_records/5”
+ delete_path = "/conservation_records/#{record_id}/external_repair_records/#{repair.id}"
+ selector = "a[data-method='delete'][href='#{delete_path}']"
+
+ # wait for the link to appear
+ expect(page).to have_selector(selector, visible: true, wait: 10)
+
+ # click it under the confirm dialog, retrying if needed
+ retries = 3
+ begin
+ accept_confirm do
+ find(selector, visible: true, wait: 10).click
+ end
+ rescue Capybara::ModalNotFound
+ retries -= 1
+ retry if retries.positive?
+ raise
end
+
+ # assert it’s gone
expect(page).to have_no_text('Wash performed by Amanda Buck', wait: 10)
+
# Conservators and Technicians
expect(page).to have_button('Add Conservators and Technicians', wait: 10)
find('button', text: 'Add Conservators and Technicians', wait: 10).click
diff --git a/spec/features/read_only_user_spec.rb b/spec/features/read_only_user_spec.rb
index cb05f9e64..c31ddc6ea 100644
--- a/spec/features/read_only_user_spec.rb
+++ b/spec/features/read_only_user_spec.rb
@@ -28,7 +28,7 @@
click_link(conservation_record.title, match: :prefer_exact)
expect(page).to have_content(conservation_record.title)
expect(page).to have_no_link('Edit Conservation Record')
- expect(page).to have_no_button('Add In-House Repairs')
+ expect(page).to have_no_button('Add In-House Repair')
expect(page).to have_no_button('Add External Repair')
expect(page).to have_no_button('Add Conservators and Technicians')
expect(page).to have_button('Save Treatment Report', disabled: true)
diff --git a/spec/features/standard_user_spec.rb b/spec/features/standard_user_spec.rb
index 94ad87cc3..05d2c3373 100644
--- a/spec/features/standard_user_spec.rb
+++ b/spec/features/standard_user_spec.rb
@@ -59,8 +59,8 @@
visit conservation_records_path
click_link(conservation_record.title, match: :prefer_exact)
- expect(page).to have_button('Add In-House Repairs')
- click_button('Add In-House Repairs')
+ expect(page).to have_button('Add In-House Repair')
+ click_button('Add In-House Repair')
select('Chuck Greenman', from: 'in_house_performed_by_user_id')
select('Soft slipcase', from: 'in_house_repair_type', match: :first)
fill_in 'in_house_other_note', with: 'Other Note'
diff --git a/spec/requests/conservation_records/index_all_conservation_records_spec.rb b/spec/requests/conservation_records/index_all_conservation_records_spec.rb
new file mode 100644
index 000000000..b0154653e
--- /dev/null
+++ b/spec/requests/conservation_records/index_all_conservation_records_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'GET /conservation_records', type: :request do
+ include_context 'conservation_records setup'
+
+ describe 'index' do
+ context 'when not signed in' do
+ include_examples 'requires authentication',
+ :conservation_records_path,
+ 'You must be signed in to access this page.'
+ end
+
+ context 'when inactive' do
+ before do
+ # pretend we’re signed in as an inactive user
+ allow_any_instance_of(ApplicationController)
+ .to receive(:current_user).and_return(inactive_user)
+
+ get conservation_records_path
+ end
+
+ it 'redirects to root with inactive alert' do
+ expect(response).to redirect_to(root_path)
+ expect(flash[:alert]).to eq('Your account is not active.')
+ end
+ end
+
+ context 'as read_only' do
+ before { login_as(read_only_user, conservation_records_path) }
+ include_examples 'index permissions', 'read_only', false, false
+ end
+
+ context 'as standard' do
+ before { login_as(standard_user, conservation_records_path) }
+ include_examples 'index permissions', 'standard', true, true
+ end
+
+ context 'as admin' do
+ before { login_as(admin_user, conservation_records_path) }
+ include_examples 'index permissions', 'admin', true, true
+ end
+ end
+end
diff --git a/spec/requests/conservation_records/show_specific_conservation_record_spec.rb b/spec/requests/conservation_records/show_specific_conservation_record_spec.rb
new file mode 100644
index 000000000..df494c6d7
--- /dev/null
+++ b/spec/requests/conservation_records/show_specific_conservation_record_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'GET /conservation_records/:id', type: :request do
+ include_context 'conservation_records setup'
+
+ let(:path) { conservation_record_path(conservation_record) }
+
+ describe 'show' do
+ context 'when not signed in' do
+ include_examples 'requires authentication',
+ :conservation_records_path,
+ 'You must be signed in to access this page.'
+ end
+
+ context 'when inactive' do
+ before do
+ # Pretend we’re already signed in as an inactive user
+ # since we can't really sign in as an inactive user.
+ # This covers a situation where a user has been made
+ # inactive while they are logged in.
+ allow_any_instance_of(ApplicationController)
+ .to receive(:current_user)
+ .and_return(inactive_user)
+ end
+
+ it 'redirects to root with inactive alert' do
+ get conservation_records_path
+ expect(response).to redirect_to(root_path)
+ expect(flash[:alert]).to eq('Your account is not active.')
+ end
+ end
+
+ context 'as read_only' do
+ before { login_as(read_only_user, path) }
+ include_examples 'show permissions', 'read_only', false, false
+ end
+
+ context 'as standard' do
+ before { login_as(standard_user, path) }
+ include_examples 'show permissions', 'standard', true, true
+
+ it 'renders report tabs' do
+ doc = Nokogiri::HTML(response.body)
+ expect(doc.at_css('ul#reportTab.nav-pills')).not_to be_nil
+ expect(
+ doc.at_xpath("//ul[@id='reportTab']//a[contains(normalize-space(.), 'Treatment Report')]")
+ ).not_to be_nil
+ expect(
+ doc.at_xpath("//ul[@id='reportTab']//a[contains(normalize-space(.), 'Abbreviated Treatment Report')]")
+ ).not_to be_nil
+ end
+
+ it 'renders cost & return form' do
+ doc = Nokogiri::HTML(response.body)
+ expect(doc.at_css('h3#cost-and-return-information')).not_to be_nil
+ expect(doc.at_css('form.disable_input')).not_to be_nil
+
+ %w[shipping_cost repair_estimate repair_cost invoice_sent_to_business_office].each do |field|
+ selector = "input[name*='cost_return_report'][name$='[#{field}]']"
+ expect(doc.at_css(selector)).not_to be_nil
+ end
+ end
+
+ it 'renders abbreviated report download link' do
+ doc = Nokogiri::HTML(response.body)
+ abr_link = doc.at_xpath(
+ "//a[contains(normalize-space(.),'Download Abbreviated Treatment Report')]"
+ )
+ expect(abr_link).not_to be_nil
+ end
+ end
+
+ context 'as admin' do
+ before { login_as(admin_user, path) }
+ include_examples 'show permissions', 'admin', true, true
+
+ it 'returns 404 for nonexistent ID' do
+ get conservation_record_path(id: 'does-not-exist')
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+ end
+end
diff --git a/spec/requests/conservation_records_spec.rb b/spec/requests/conservation_records_spec.rb
deleted file mode 100644
index 2f7bb7fbd..000000000
--- a/spec/requests/conservation_records_spec.rb
+++ /dev/null
@@ -1,328 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-require 'nokogiri'
-
-RSpec.describe 'ConservationRecords', type: :request do
- let(:read_only_user) { create(:user, role: 'read_only') }
- let(:standard_user) { create(:user, role: 'standard') }
- let(:admin_user) { create(:user, role: 'admin') }
- let(:inactive_user) { create(:user, role: 'standard', account_active: false) }
- let!(:conservation_record) { create(:conservation_record) }
-
- describe 'GET /conservation_records' do
- context 'when user is read_only' do
- before do
- request_login_as(read_only_user, target: conservation_records_path)
- end
-
- it 'shows the conservation records page with appropriate content for the user' do
- expect(response).to have_http_status(200)
-
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- # Check for the presence of an h1 with the text "Conservation Records"
- expect(parsed_body.at_css('h1').text).to eq('Conservation Records')
-
- # Check for the presence of specific content within the parsed HTML
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
-
- # Check for the absence of a specific link with the text "New Conservation Record"
- expect(parsed_body.at_css('a:contains("New Conservation Record")')).to be_nil
-
- # Check for the presence of a link with the conservation record's title and correct href
- expect(parsed_body.at_css("a[href='#{conservation_record_path(conservation_record)}']").text).to eq(conservation_record.title)
-
- # Check for the absence of an img tag with a delete icon and alt="Delete"
- expect(parsed_body.at_css('img.delete-icon[alt="Delete"]')).to be_nil
- end
- end
-
- context 'when user is standard' do
- before do
- request_login_as(standard_user, target: conservation_records_path)
- end
-
- it 'shows the conservation records page with appropriate content for the user' do
- expect(response).to have_http_status(200)
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- # Check for the presence of an h1 with the text "Conservation Records"
- expect(parsed_body.at_css('h1').text).to eq('Conservation Records')
-
- # Check for the presence of specific content within the parsed HTML
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
-
- # Check for the presence of a specific link with the text "New Conservation Record"
- expect(parsed_body.at_css('a:contains("New Conservation Record")')).not_to be_nil
-
- # Check for the presence of a link with the conservation record's title and correct href
- expect(parsed_body.at_css("a[href='#{conservation_record_path(conservation_record)}']").text).to eq(conservation_record.title)
-
- # Check for the presence of an img tag with a delete icon and alt="Delete"
- expect(parsed_body.at_css('img.delete-icon[alt="Delete"]')).not_to be_nil
- end
- end
-
- context 'when user is admin' do
- before do
- request_login_as(admin_user, target: conservation_records_path)
- end
-
- it 'shows the conservation records page with appropriate content for the user' do
- expect(response).to have_http_status(200)
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- # Check for the presence of an h1 with the text "Conservation Records"
- expect(parsed_body.at_css('h1').text).to eq('Conservation Records')
-
- # Check for the presence of specific text content within the parsed HTML
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
-
- # Check for the presence of a link with href for creating a new conservation record
- expect(parsed_body.at_css("a[href='#{new_conservation_record_path}']").text).to eq('New Conservation Record')
-
- # Check for the presence of a link with the conservation record's title and correct href
- expect(parsed_body.at_css("a[href='#{conservation_record_path(conservation_record)}']").text).to eq(conservation_record.title)
-
- # Check for the presence of an img tag with a delete icon and alt="Delete"
- expect(parsed_body.at_css('img.delete-icon[alt="Delete"]')).not_to be_nil
- end
- end
-
- context 'when user is not logged in' do
- it 'redirects to root page' do
- get controlled_vocabularies_path
- expect(response).to redirect_to(root_path)
- expect(flash[:alert]).to eq('You must be signed in to access this page.')
- end
- end
-
- context 'when user is inactive' do
- before do
- request_login_as(inactive_user, target: controlled_vocabularies_path)
- end
-
- it 'shows an alert' do
- expect(response).to redirect_to root_path
- expect(flash[:alert]).to eq('Your account is not active.')
- end
- end
- end
-
- describe 'GET /conservation_records/:id' do
- context 'when user is read_only' do
- before do
- request_login_as(read_only_user, target: conservation_record_path(conservation_record))
- end
-
- it 'shows the conservation record with appropriate permissions' do
- expect(response).to have_http_status(200)
-
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- expect(parsed_body.at_css("a[href='#{conservation_records_path}']:contains('← Return to List')")).not_to be_nil
- expect(parsed_body.at_css('h1').text).to include(conservation_record.title)
- expect(parsed_body.at_css('a:contains("Edit Conservation Record")')).to be_nil
- expect(parsed_body.at_css('a:contains("Download Conservation Worksheet")')).not_to be_nil
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
- expect(parsed_body.at_css('h3:contains("In-House Repairs")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Add In-House Repairs")')).to be_nil
- expect(parsed_body.at_css('h3:contains("External Repairs")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Add External Repair")')).to be_nil
- expect(parsed_body.at_css('h3:contains("Conservators and Technicians")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Add Conservators and Technicians")')).to be_nil
- expect(parsed_body.at_css('h3:contains("Treatment Report")')).not_to be_nil
- expect(parsed_body.at_css('h3:contains("Cost and Return Information")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Download Abbreviated Treatment Report")')).not_to be_nil
- end
- end
-
- context 'when user is standard' do
- before do
- request_login_as(standard_user, target: conservation_record_path(conservation_record))
- end
-
- it 'shows the conservation record with appropriate permissions' do
- expect(response).to have_http_status(200)
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- # Check for the presence of a link with href for returning to the list
- expect(parsed_body.at_css("a[href='#{conservation_records_path}'][text()='← Return to List']")).not_to be_nil
-
- # Check for the presence of an h1 with the conservation record title
- expect(parsed_body.at_css('h1').text).to eq(conservation_record.title)
-
- # Check for the presence of a link to edit the conservation record
- expect(parsed_body.at_css("a[href='#{edit_conservation_record_path(conservation_record)}']").text).to eq('Edit Conservation Record')
-
- # Check for the presence of a link to download the conservation worksheet
- expect(parsed_body.at_css('a:contains("Download Conservation Worksheet")')).not_to be_nil
-
- # Check for the presence of specific text content within the parsed HTML
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
-
- expect(parsed_body.at_css('h3:contains("In-House Repairs")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#inHouseRepairModal"]'
- ).text.strip
- ).to eq('Add In-House Repairs')
-
- expect(parsed_body.at_css('h3:contains("External Repairs")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#externalRepairModal"]'
- ).text.strip
- ).to eq('Add External Repair')
-
- expect(parsed_body.at_css('h3:contains("Conservators and Technicians")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#ConservatorsTechniciansModal"]'
- ).text.strip
- ).to eq('Add Conservators and Technicians')
-
- expect(parsed_body.at_css('h3:contains("Treatment Report")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'input[type="submit"]' \
- '[name="commit"]' \
- '[value="Save Treatment Report"]' \
- '[class="btn btn-primary"]' \
- '[data-disable-with="Save Treatment Report"]'
- )
- ).not_to be_nil
-
- expect(parsed_body.at_css('h3:contains("Cost and Return Information")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Download Abbreviated Treatment Report")')).not_to be_nil
- end
- end
-
- context 'when user is admin' do
- before do
- request_login_as(admin_user, target: conservation_record_path(conservation_record))
- end
-
- it 'shows the conservation record with appropriate permissions' do
- expect(response).to have_http_status(200)
- # Parse the response body as HTML
- parsed_body = Nokogiri::HTML(response.body)
-
- # Check for the presence of a link with href for returning to the list
- expect(parsed_body.at_css("a[href='#{conservation_records_path}'][text()='← Return to List']")).not_to be_nil
-
- # Check for the presence of an h1 with the conservation record title
- expect(parsed_body.at_css('h1').text).to eq(conservation_record.title)
-
- # Check for the presence of a link to edit the conservation record
- expect(parsed_body.at_css("a[href='#{edit_conservation_record_path(conservation_record)}']").text).to eq('Edit Conservation Record')
-
- # Check for the presence of a link to download the conservation worksheet
- expect(parsed_body.at_css('a:contains("Download Conservation Worksheet")')).not_to be_nil
-
- # Check for the presence of specific text content within the parsed HTML
- expect(parsed_body.text).to include(conservation_record.id.to_s)
- expect(parsed_body.text).to include(conservation_record.title)
- expect(parsed_body.text).to include(conservation_record.author)
- expect(parsed_body.text).to include(conservation_record.call_number.to_s)
- expect(parsed_body.text).to include(conservation_record.item_record_number.to_s)
-
- expect(parsed_body.at_css('h3:contains("In-House Repairs")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#inHouseRepairModal"]'
- ).text.strip
- ).to eq('Add In-House Repairs')
-
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#externalRepairModal"]'
- ).text.strip
- ).to eq('Add External Repair')
-
- expect(parsed_body.at_css('h3:contains("External Repairs")')).not_to be_nil
- expect(parsed_body.at_css('h3:contains("Conservators and Technicians")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'button.btn.btn-primary.cta-btn' \
- '[data-bs-toggle="modal"]' \
- '[data-bs-target="#ConservatorsTechniciansModal"]'
- ).text.strip
- ).to eq('Add Conservators and Technicians')
-
- expect(parsed_body.at_css('h3:contains("Treatment Report")')).not_to be_nil
- expect(
- parsed_body.at_css(
- 'input[type="submit"][name="commit"]' \
- '[value="Save Treatment Report"]' \
- '[class="btn btn-primary"]' \
- '[data-disable-with="Save Treatment Report"]'
- )
- ).not_to be_nil
-
- expect(parsed_body.at_css('h3:contains("Cost and Return Information")')).not_to be_nil
- expect(parsed_body.at_css('a:contains("Download Abbreviated Treatment Report")')).not_to be_nil
- end
-
- it 'returns a 404 for a non-existent conservation record' do
- get conservation_record_path(id: 'non-existent-id')
- expect(response).to have_http_status(404)
- end
- end
-
- context 'when user is not logged in' do
- it 'redirects to root page' do
- get conservation_record_path(conservation_record)
- expect(response).to redirect_to(root_path)
- expect(flash[:alert]).to eq('You must be signed in to access this page.')
- end
- end
-
- context 'when user is inactive' do
- before do
- request_login_as(inactive_user, target: conservation_record_path(conservation_record))
- end
-
- it 'redirects to root path with alert' do
- expect(response).to redirect_to(root_path)
- expect(flash[:alert]).to eq('Your account is not active.')
- end
- end
- end
-end
diff --git a/spec/support/conservation_records_shared_examples.rb b/spec/support/conservation_records_shared_examples.rb
new file mode 100644
index 000000000..3db1ff3f0
--- /dev/null
+++ b/spec/support/conservation_records_shared_examples.rb
@@ -0,0 +1,116 @@
+# spec/support/conservation_records_shared_examples.rb
+# frozen_string_literal: true
+
+require 'nokogiri'
+
+RSpec.shared_context 'conservation_records setup' do
+ let(:read_only_user) { create(:user, role: 'read_only') }
+ let(:standard_user) { create(:user, role: 'standard') }
+ let(:admin_user) { create(:user, role: 'admin') }
+ let(:inactive_user) { create(:user, role: 'standard', account_active: false) }
+ let!(:conservation_record) { create(:conservation_record) }
+
+ let(:record_texts) do
+ [
+ conservation_record.id.to_s,
+ conservation_record.title,
+ conservation_record.author,
+ conservation_record.call_number.to_s,
+ conservation_record.item_record_number.to_s
+ ]
+ end
+
+ def login_as(user, path)
+ request_login_as(user, target: path)
+ end
+end
+
+RSpec.shared_examples 'requires authentication' do |path_method, *args, flash_message|
+ it 'redirects to root with alert' do
+ get send(path_method, *args)
+ expect(response).to redirect_to(root_path)
+ expect(flash[:alert]).to eq(flash_message)
+ end
+end
+
+RSpec.shared_examples 'index permissions' do |role, can_create, can_delete|
+ it "#{role} sees correct index controls" do
+ expect(response).to have_http_status(:ok)
+ doc = Nokogiri::HTML(response.body)
+
+ # header and list contents
+ expect(doc.at_css('h1').text).to eq('Conservation Records')
+ record_texts.each { |txt| expect(doc.text).to include(txt) }
+
+ # New link
+ if can_create
+ expect(doc.at_css("a[href='#{new_conservation_record_path}']")).not_to be_nil
+ else
+ expect(doc.at_xpath("//a[text()='New Conservation Record']")).to be_nil
+ end
+
+ # Delete icons
+ imgs = doc.css('img.delete-icon[alt="Delete"]')
+ expect(imgs.any?).to eq(can_delete)
+ end
+end
+
+RSpec.shared_examples 'show permissions' do |role, can_edit, can_add|
+ it "#{role} sees correct show controls" do
+ expect(response).to have_http_status(:ok)
+ doc = Nokogiri::HTML(response.body)
+
+ # back‐to‐list link
+ return_link = doc.at_xpath(
+ "//a[@href='#{conservation_records_path}' and contains(normalize-space(.),'← Return to List')]"
+ )
+ expect(return_link).not_to be_nil
+
+ # page title
+ expect(doc.at_css('h1').text).to include(conservation_record.title)
+
+ # worksheet download link
+ worksheet_link = doc.at_xpath(
+ "//a[contains(normalize-space(.), 'Download Conservation Worksheet')]"
+ )
+ expect(worksheet_link).not_to be_nil
+
+ # edit link
+ if can_edit
+ edit_link = doc.at_css("a[href='#{edit_conservation_record_path(conservation_record)}']")
+ expect(edit_link).not_to be_nil
+ expect(edit_link.text.strip).to eq('Edit Conservation Record')
+ else
+ expect(
+ doc.at_xpath("//a[contains(normalize-space(.), 'Edit Conservation Record')]")
+ ).to be_nil
+ end
+
+ # detail fields
+ record_texts.each { |txt| expect(doc.text).to include(txt) }
+
+ # repair-section headers & modal triggers
+ {
+ 'In-House Repairs' => 'inHouseRepairModal',
+ 'External Repairs' => 'externalRepairModal',
+ 'Conservators and Technicians' => 'ConservatorsTechniciansModal'
+ }.each do |section_text, modal_id|
+ # header
+ header_xpath = "//h3[contains(normalize-space(.), '#{section_text}')]"
+ expect(doc.at_xpath(header_xpath)).not_to be_nil
+
+ # modal-trigger button by data attribute
+ btn_selector = "button.cta-btn[data-bs-toggle='modal'][data-bs-target='##{modal_id}']"
+ if can_add
+ expect(doc.at_css(btn_selector)).not_to be_nil
+ else
+ expect(doc.at_css(btn_selector)).to be_nil
+ end
+ end
+
+ # modal containers always present in DOM
+ expect(doc.at_css('#inHouseRepairModal.modal')).not_to be_nil
+ expect(doc.at_css('#externalRepairModal.modal')).not_to be_nil
+ expect(doc.at_css('#ConservatorsTechniciansModal.modal')).not_to be_nil
+ end
+end
diff --git a/spec/views/conservation_records/show.html.erb_spec.rb b/spec/views/conservation_records/show.html.erb_spec.rb
index 7e75bf301..045c4f903 100644
--- a/spec/views/conservation_records/show.html.erb_spec.rb
+++ b/spec/views/conservation_records/show.html.erb_spec.rb
@@ -54,7 +54,7 @@
view_stub_authorization(user)
render
expect(rendered).not_to have_link('Edit Conservation Record')
- expect(rendered).not_to have_button('Add In-House Repairs')
+ expect(rendered).not_to have_button('Add In-House Repair')
expect(rendered).not_to have_button('Add External Repair')
expect(rendered).not_to have_button('Add Conservators and Technicians')
expect(rendered).not_to have_button('Delete In-House Repair')