diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 02d21c10..3a6f708f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,28 +1,49 @@ name: CI GitHub Action -on: "push" -#permissions: -# contents: read +on: push jobs: test: runs-on: ubuntu-latest + services: + selenium: + image: selenium/standalone-chrome + ports: + - 4444:4444 + steps: - uses: actions/checkout@v3 + - name: Set up Ruby - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): uses: ruby/setup-ruby@v1 with: - bundler-cache: true # runs 'bundle install' and caches installed gems automatically + ruby-version: '3.3.3' # or your preferred Ruby version + bundler-cache: true + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y xvfb libnss3-dev + gem install bundler + bundle install + - name: Run rubocop run: | bundle exec rubocop + - name: Run tests - run: bundle exec rake + env: + DISPLAY: :99.0 + run: | + Xvfb :99 -ac & + bundle exec rspec + - name: Coveralls uses: coverallsapp/github-action@v2 + - name: Brakeman linter run: bundle exec brakeman --no-exit-on-warn --no-exit-on-error + - name: 'Run Bundler Audit' run: bundle exec bundler-audit + diff --git a/app/assets/javascripts/filtermanagement.js b/app/assets/javascripts/filtermanagement.js new file mode 100644 index 00000000..fa499f03 --- /dev/null +++ b/app/assets/javascripts/filtermanagement.js @@ -0,0 +1,17 @@ +// Specific to software_records.html.erb +function clearFilters() { + document.getElementById("vendor-record-filter").style.display = "none"; + document.getElementById("software-type-filter").style.display = "none"; + var loaded = window.location.host; + window.location = "software_records"; +} + +function handleRadio(myRadio) { + if(myRadio.value === "vendor_records") { + document.getElementById("vendor-record-filter").style.display = "block"; + document.getElementById("software-type-filter").style.display = "none"; + } else { + document.getElementById("vendor-record-filter").style.display = "none"; + document.getElementById("software-type-filter").style.display = "block"; + } +} diff --git a/app/assets/javascripts/inputsanitization.js b/app/assets/javascripts/inputsanitization.js new file mode 100644 index 00000000..3cb7bc43 --- /dev/null +++ b/app/assets/javascripts/inputsanitization.js @@ -0,0 +1,4 @@ +var createdbyfield = document.getElementsByClassName('regex-createdby')[0]; +createdbyfield.onkeyup = function() { + createdbyfield.value = createdbyfield.value.replace(/[^a-zA-Z0-9 ]/, ''); +} diff --git a/app/assets/javascripts/multivalueinputs.js b/app/assets/javascripts/multivalueinputs.js new file mode 100644 index 00000000..7a671b29 --- /dev/null +++ b/app/assets/javascripts/multivalueinputs.js @@ -0,0 +1,78 @@ +window.onload = function() { + window.counts = { + + developers: window.counts?.developers || 1, + tech_leads: window.counts?.tech_leads || 1, + departments: window.counts?.departments || 1, + product_owners: window.counts?.product_owners || 1, + admin_users: window.counts?.admin_users || 1 + + }; +}; + +function add(name, value) { + var count = window.counts[name]++; + var elementId = name + count; + var inputId = "software_record_" + name + "_" + count; + + var element = document.createElement("div"); + element.className = "input-group mt-2"; + element.id = elementId; + + var inputElement = document.createElement("input"); + inputElement.type = "text"; + inputElement.required = true; + inputElement.name = "software_record[" + name + "][]"; + inputElement.id = inputId; // Ensure unique ID for each input element + inputElement.className = "form-control"; + if (value != "") { + inputElement.value = value; + } + element.appendChild(inputElement); + + var inputGroupAppend = document.createElement("div"); + inputGroupAppend.className = "input-group-append btnRemove"; + element.appendChild(inputGroupAppend); + + var spanElement = document.createElement("span"); + spanElement.className = "input-group-text"; + inputGroupAppend.appendChild(spanElement); + + var removeButton = document.createElement("i"); + removeButton.className = "fas fa-minus remove"; + removeButton.innerHTML = " Delete"; + spanElement.appendChild(removeButton); + + // Update the onclick handler to directly remove the parent element + inputGroupAppend.onclick = function() { + element.remove(); + }; + + var valued = "multiple_" + name; + var multiValued = document.getElementById(valued); + multiValued.appendChild(element); +} + +document.getElementById("btnAddProductOwners").onclick = function() { + add("product_owners", ""); +}; + +document.getElementById("btnAddAdminUsers").onclick = function() { + add("admin_users", ""); +}; + +document.getElementById("btnAddDepartments").onclick = function() { + add("departments", ""); +}; + +document.getElementById("btnAddDevelopers").onclick = function() { + add("developers", ""); +}; + +document.getElementById("btnAddTechLeads").onclick = function() { + add("tech_leads", ""); +}; + +function remove(id) { + document.getElementById(id).remove(); +} diff --git a/app/assets/javascripts/navigation.js b/app/assets/javascripts/navigation.js new file mode 100644 index 00000000..777f8df1 --- /dev/null +++ b/app/assets/javascripts/navigation.js @@ -0,0 +1,11 @@ +function openNav() { + document.getElementById("mySidenav").style.visibility = "visible"; + document.getElementById("mySidenav").style.width = "250px"; + document.getElementById("main").style.marginLeft = "250px"; +} + +function closeNav() { + document.getElementById("mySidenav").style.visibility = "hidden"; + document.getElementById("mySidenav").style.width = "0"; + document.getElementById("main").style.marginLeft = "0"; +} diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index bbec0fdd..dbc2b4cb 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,138 +6,33 @@ <%= csp_meta_tag %> <%= favicon_link_tag asset_path('favicon.ico'), :rel => 'icon', :type => 'image/x-icon' %> - + - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> - - <%= render 'shared/dashboard_menu' %> -
- <%= render 'shared/dashboard_header' %> - <% if current_user %>
Menu ☰
<% end %> - <%= yield %> - <%= alerts %> - <% if @controller == "users" %> - <% %> - <% elsif @controller!=("front") %> - <%= render 'shared/application_footer' %> - <% end %> -
+ <%= render 'shared/dashboard_menu' %> +
+ <%= render 'shared/dashboard_header' %> + <% if current_user %> +
+ Menu ☰ +
+ <% end %> + <%= yield %> + <%= alerts %> + <% if @controller == "users" %> + <% %> + <% elsif @controller!=("front") %> + <%= render 'shared/application_footer' %> + <% end %> +
+ <%= javascript_include_tag 'navigation' %> + <%= javascript_include_tag 'filtermanagement' %> + <%= javascript_include_tag 'inputsanitization' %> + <%= javascript_include_tag 'multivalueinputs' %> - - - + diff --git a/app/views/layouts/software_records.html.erb b/app/views/layouts/software_records.html.erb index a314361c..4b338990 100644 --- a/app/views/layouts/software_records.html.erb +++ b/app/views/layouts/software_records.html.erb @@ -6,179 +6,29 @@ <%= csp_meta_tag %> <%= favicon_link_tag asset_path('favicon.ico'), :rel => 'icon', :type => 'image/x-icon' %> - + - <%= stylesheet_link_tag 'software_records', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= stylesheet_link_tag 'software_records', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> - - <%= render 'shared/dashboard_menu' %> -
- <%= render 'shared/dashboard_header' %> - <% if current_user %>
Menu ☰
<% end %> - <%= yield %> - <%= alerts %> - <%= render 'shared/dashboard_footer' %> -
+ <%= render 'shared/dashboard_menu' %> +
+ <%= render 'shared/dashboard_header' %> + <% if current_user %> +
+ Menu ☰ +
+ <% end %> + <%= yield %> + <%= alerts %> + <%= render 'shared/dashboard_footer' %> +
+ <%= javascript_include_tag 'navigation' %> + <%= javascript_include_tag 'filtermanagement' %> + <%= javascript_include_tag 'inputsanitization' %> + <%= javascript_include_tag 'multivalueinputs' %> - - - - + diff --git a/app/views/software_records/_form_general.html.erb b/app/views/software_records/_form_general.html.erb index d4a2f793..14a55089 100644 --- a/app/views/software_records/_form_general.html.erb +++ b/app/views/software_records/_form_general.html.erb @@ -47,305 +47,13 @@ <%= form.select(:authentication_type, t('authentication_types'), {}, { :class => 'form-control' }) %> - - <% if current_user.role.to_s == "root_admin" %> - <% if component.to_s == "new" %> -
- <%= form.label :departments %> + add more -
- <%= text_field_tag "software_record[departments][]", nil, required: true, class: "form-control" %> -
- Delete -
-
-
- <% else %> - <% if @software_record.departments.count != 0 %> -
- <%= form.label :departments %> + add more - <% @software_record.departments.each do |department| %> - <% if @count_departments == 2 %> -
- <%= text_field_tag "software_record[departments][]", department, required: true, class: "form-control" %> -
- Delete -
-
- <% @count_departments += 1 %> - <% else %> - <% @id = "software_record_departments_" + @count_departments.to_s %> -
- <%= text_field_tag "software_record[departments][]", department, id: "software_record_departments_", required: true, class: "form-control" %> -
- Delete -
-
- <% end %> - <% end %> -
- <% else %> -
- <%= form.label :departments %> + add more -
- <% end %> - <% end %> - <% else %> - <% if @software_record.departments.count != 0 %> -
- <% @software_record.departments.each do |department| %> - <% if @count_departments == 2 %> - - <% @count_departments += 1 %> - <% else %> - <% @id = "software_record_departments_" + @count_departments.to_s %> - - <% end %> - <% end %> -
- <% end %> - <% end %> - - - - - <% if current_user.role.to_s != "owner" %> - <% if component.to_s == "new" %> -
- <%= form.label :developers %> + add more -
- <%= text_field_tag "software_record[developers][]", nil, required: true, class: "form-control" %> -
- Delete -
-
-
- <% else %> - <% if @software_record.developers.count != 0 %> -
- <%= form.label :developers %> + add more - <% @software_record.developers.each do |developer| %> - <% if @count_developers == 2 %> -
- <%= text_field_tag "software_record[developers][]", developer, required: true, class: "form-control" %> -
- Delete -
-
- <% @count_developers += 1 %> - <% else %> - <% @id = "software_record_developers_" + @count_developers.to_s %> -
- <%= text_field_tag "software_record[developers][]", developer, id: "software_record_developers_", required: true, class: "form-control" %> -
- Delete -
-
- <% end %> - <% end %> -
- <% else %> -
- <%= form.label :developers %> + add more -
- <% end %> - <% end %> - <% else %> - <% if @software_record.developers.count != 0 %> -
- <% @software_record.developers.each do |developer| %> - <% if @count_developers == 2 %> - - <% @count_developers += 1 %> - <% else %> - <% @id = "software_record_developers_" + @count_developers.to_s %> - - <% end %> - <% end %> -
- <% end %> - <% end %> - - - - - <% if current_user.role.to_s != "owner" %> - <% if component.to_s == "new" %> -
- <%= form.label :tech_leads %> + add more -
- <%= text_field_tag "software_record[tech_leads][]", nil, required: true, class: "form-control" %> -
- Delete -
-
-
- <% else %> - <% if @software_record.tech_leads.count != 0 %> -
- <%= form.label :tech_leads %> + add more - <% @software_record.tech_leads.each do |tech_lead| %> - <% if @count_tech_leads == 2 %> -
- <%= text_field_tag "software_record[tech_leads][]", tech_lead, required: true, class: "form-control" %> -
- Delete -
-
- <% @count_tech_leads += 1 %> - <% else %> - <% @id = "software_record_tech_leads_" + @count_tech_leads.to_s %> -
- <%= text_field_tag "software_record[tech_leads][]", tech_lead, id: "software_record_tech_leads_", required: true, class: "form-control" %> -
- Delete -
-
- <% end %> - <% end %> -
- <% else %> -
- <%= form.label :tech_leads %> + add more -
- <% end %> - <% end %> - <% else %> - <% if @software_record.tech_leads.count != 0 %> -
- <% @software_record.tech_leads.each do |tech_lead| %> - <% if @count_tech_leads == 2 %> - - <% @count_tech_leads += 1 %> - <% else %> - <% @id = "software_record_tech_leads_" + @count_tech_leads.to_s %> - - <% end %> - <% end %> -
- <% end %> - <% end %> - - - - - - <% if component.to_s == "new" %> -
- <%= form.label "Product Lead/Contact" %> + add more -
- <%= text_field_tag "software_record[product_owners][]", nil, required: true, class: "form-control" %> -
- Delete -
-
-
- <% else %> - <% if @software_record.product_owners.count != 0 %> -
- <%= form.label :product_owners %> + add more - <% @software_record.product_owners.each do |product_owner| %> - <% if @count_product_owners == 2 %> -
- <%= text_field_tag "software_record[product_owners][]", product_owner, required: true, class: "form-control" %> -
- Delete -
-
- <% @count_product_owners += 1 %> - <% else %> - <% @id = "software_record_tech_leads_" + @count_product_owners.to_s %> -
- <%= text_field_tag "software_record[product_owners][]", product_owner, id: "software_record_product_owners_", required: true, class: "form-control" %> -
- Delete -
-
- <% end %> - <% end %> -
- <% else %> -
- <%= form.label :product_owners %> + add more -
- <% end %> - <% end %> - - - - - - <% if component.to_s == "new" %> -
- <%= form.label "Admin Users" %> + add more -
- <%= text_field_tag "software_record[admin_users][]", nil, required: true, class: "form-control" %> -
- Delete -
-
-
- <% else %> - <% if @software_record.admin_users.count != 0 %> -
- <%= form.label :admin_users %> + add more - <% @software_record.admin_users.each do |admin_user| %> - <% if @count_admin_users == 2 %> -
- <%= text_field_tag "software_record[admin_users][]", admin_user, required: true, class: "form-control" %> -
- Delete -
-
- <% @count_admin_users += 1 %> - <% else %> - <% @id = "software_record_admin_users" + @count_admin_users.to_s %> -
- <%= text_field_tag "software_record[admin_users][]", admin_user, id: "software_record_admin_users", required: true, class: "form-control" %> -
- Delete -
-
- <% end %> - <% end %> -
- <% else %> -
- <%= form.label :admin_users %> + add more -
- <% end %> - <% end %> + + <%= render 'form_multi_departments', software_record: @software_record, component: component, form: form %> + <%= render 'form_multi_developers', software_record: @software_record, component: component, form: form %> + <%= render 'form_multi_tech_leads', software_record: @software_record, component: component, form: form %> + <%= render 'form_multi_product_owners', software_record: @software_record, component: component, form: form %> + <%= render 'form_multi_admin_users', software_record: @software_record, component: component, form: form %> - <% if current_user.role.to_s != "owner" %>
diff --git a/app/views/software_records/_form_multi_admin_users.html.erb b/app/views/software_records/_form_multi_admin_users.html.erb new file mode 100644 index 00000000..ebaff8bd --- /dev/null +++ b/app/views/software_records/_form_multi_admin_users.html.erb @@ -0,0 +1,29 @@ + +
+ <%= form.label "Admin Users" %> + + add more + + <% if component.to_s == "new" || @software_record.admin_users.empty? %> +
+ <%= text_field_tag "software_record[admin_users][]", nil, required: true, class: "form-control" %> +
+ Delete +
+
+ <% else %> + <% @software_record.admin_users.each_with_index do |admin_user, index| %> + <% @id = "admin_users#{index + 1}" %> +
+ <%= text_field_tag "software_record[admin_users][]", admin_user, required: true, class: "form-control" %> +
+ Delete +
+
+ <% end %> + <% end %> +
+ + + + + diff --git a/app/views/software_records/_form_multi_departments.html.erb b/app/views/software_records/_form_multi_departments.html.erb new file mode 100644 index 00000000..a2912545 --- /dev/null +++ b/app/views/software_records/_form_multi_departments.html.erb @@ -0,0 +1,44 @@ + +<% if current_user.role.to_s == "root_admin" %> +
+ <%= form.label :departments %> + + add more + + <% if component.to_s == "new" || @software_record.departments.empty? %> +
+ <%= text_field_tag "software_record[departments][]", nil, required: true, class: "form-control" %> +
+ Delete +
+
+ <% else %> + <% @software_record.departments.each_with_index do |department, index| %> + <% @id = "departments#{index + 1}" %> +
+ <%= text_field_tag "software_record[departments][]", department, required: true, class: "form-control" %> +
+ Delete +
+
+ <% end %> + <% end %> +
+<% else %> + <% if @software_record.departments.any? %> +
+ <% @software_record.departments.each_with_index do |department, index| %> + <% @id = "departments#{index + 1}" %> + + <% end %> +
+ <% end %> +<% end %> + + + + diff --git a/app/views/software_records/_form_multi_developers.html.erb b/app/views/software_records/_form_multi_developers.html.erb new file mode 100644 index 00000000..6bb8f308 --- /dev/null +++ b/app/views/software_records/_form_multi_developers.html.erb @@ -0,0 +1,43 @@ + +<% if current_user.role.to_s != "owner" %> +
+ <%= form.label :developers %> + + add more + + <% if component.to_s == "new" || @software_record.developers.empty? %> +
+ <%= text_field_tag "software_record[developers][]", nil, required: true, class: "form-control" %> +
+ Delete +
+
+ <% else %> + <% @software_record.developers.each_with_index do |developer, index| %> + <% @id = "developers#{index + 1}" %> +
+ <%= text_field_tag "software_record[developers][]", developer, required: true, class: "form-control" %> +
+ Delete +
+
+ <% end %> + <% end %> +
+<% else %> + <% if @software_record.developers.any? %> +
+ <% @software_record.developers.each_with_index do |developer, index| %> + <% @id = "developers#{index + 1}" %> + + <% end %> +
+ <% end %> +<% end %> + + + diff --git a/app/views/software_records/_form_multi_product_owners.html.erb b/app/views/software_records/_form_multi_product_owners.html.erb new file mode 100644 index 00000000..7bfec3c0 --- /dev/null +++ b/app/views/software_records/_form_multi_product_owners.html.erb @@ -0,0 +1,29 @@ + +
+ <%= form.label "Product Lead/Contact" %> + + add more + + <% if component.to_s == "new" || @software_record.product_owners.empty? %> +
+ <%= text_field_tag "software_record[product_owners][]", nil, required: true, class: "form-control" %> +
+ Delete +
+
+ <% else %> + <% @software_record.product_owners.each_with_index do |product_owner, index| %> + <% @id = "product_owners#{index + 1}" %> +
+ <%= text_field_tag "software_record[product_owners][]", product_owner, required: true, class: "form-control" %> +
+ Delete +
+
+ <% end %> + <% end %> +
+ + + + + diff --git a/app/views/software_records/_form_multi_tech_leads.html.erb b/app/views/software_records/_form_multi_tech_leads.html.erb new file mode 100644 index 00000000..09b7c0bc --- /dev/null +++ b/app/views/software_records/_form_multi_tech_leads.html.erb @@ -0,0 +1,41 @@ + +<% if current_user.role.to_s != "owner" %> +
+ <%= form.label :tech_leads %> + + add more + + <% if component.to_s == "new" || @software_record.tech_leads.empty? %> +
+ <%= text_field_tag "software_record[tech_leads][]", nil, required: true, class: "form-control" %> +
+ Delete +
+
+ <% else %> + <% @software_record.tech_leads.each_with_index do |tech_lead, index| %> + <% @id = "tech_leads#{index + 1}" %> +
+ <%= text_field_tag "software_record[tech_leads][]", tech_lead, required: true, class: "form-control" %> +
+ Delete +
+
+ <% end %> + <% end %> +
+<% else %> + <% if @software_record.tech_leads.any? %> +
+ <% @software_record.tech_leads.each_with_index do |tech_lead, index| %> + <% @id = "tech_leads#{index + 1}" %> + + <% end %> +
+ <% end %> +<% end %> + diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 13a8f3f2..fc33fc35 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -14,3 +14,7 @@ # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) +Rails.application.config.assets.precompile += %w[navigation.js] +Rails.application.config.assets.precompile += %w[filtermanagement.js] +Rails.application.config.assets.precompile += %w[inputsanitization.js] +Rails.application.config.assets.precompile += %w[multivalueinputs.js] diff --git a/spec/features/software_records/multi_value_spec.rb b/spec/features/software_records/multi_value_spec.rb new file mode 100644 index 00000000..283ed91a --- /dev/null +++ b/spec/features/software_records/multi_value_spec.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.feature 'MultiValueFields', type: :feature, js: true do + let(:user) { FactoryBot.create(:admin) } + let(:software_record) { FactoryBot.create(:software_record) } + + before do + visit new_user_session_path + fill_in 'Email', with: user.email + fill_in 'Password', with: 'random1234' + click_button 'Login' + visit edit_software_record_path(software_record) + end + + scenario 'User can add and remove multiple developers' do + within('#multiple_developers') do + expect(page).to have_selector('.input-group', count: 1) + find('a', text: '+ add more').click + find('a', text: '+ add more').click + + # Fill in fields with identifiable values + all('.input-group input').each_with_index do |input, index| + input.set("Developer #{index + 1}") + end + + expect(page).to have_selector('.input-group', count: 3) + + # Delete 'Developer 2' + all('.input-group-append')[1].click + expect(page).to have_selector('.input-group', count: 2) + expect(page).not_to have_field(with: 'Developer 2') + expect(page).to have_field(with: 'Developer 1') + expect(page).to have_field(with: 'Developer 3') + end + end + + scenario 'User can add and remove multiple tech leads' do + within('#multiple_tech_leads') do + expect(page).to have_selector('.input-group', count: 1) + find('a', text: '+ add more').click + find('a', text: '+ add more').click + + # Fill in fields with identifiable values + all('.input-group input').each_with_index do |input, index| + input.set("Tech Lead #{index + 1}") + end + + expect(page).to have_selector('.input-group', count: 3) + + # Delete 'Tech Lead 2' + all('.input-group-append')[1].click + expect(page).to have_selector('.input-group', count: 2) + expect(page).not_to have_field(with: 'Tech Lead 2') + expect(page).to have_field(with: 'Tech Lead 1') + expect(page).to have_field(with: 'Tech Lead 3') + end + end + + scenario 'User can add and remove multiple departments' do + within('#multiple_departments') do + expect(page).to have_selector('.input-group', count: 1) + find('a', text: '+ add more').click + find('a', text: '+ add more').click + + # Fill in fields with identifiable values + all('.input-group input').each_with_index do |input, index| + input.set("Department #{index + 1}") + end + + expect(page).to have_selector('.input-group', count: 3) + + # Delete 'Department 2' + all('.input-group-append')[1].click + expect(page).to have_selector('.input-group', count: 2) + expect(page).not_to have_field(with: 'Department 2') + expect(page).to have_field(with: 'Department 1') + expect(page).to have_field(with: 'Department 3') + end + end + + scenario 'User can add and remove multiple product owners' do + within('#multiple_product_owners') do + expect(page).to have_selector('.input-group', count: 1) + find('a', text: '+ add more').click + find('a', text: '+ add more').click + + # Fill in fields with identifiable values + all('.input-group input').each_with_index do |input, index| + input.set("Product Owner #{index + 1}") + end + + expect(page).to have_selector('.input-group', count: 3) + + # Delete 'Product Owner 2' + all('.input-group-append')[1].click + expect(page).to have_selector('.input-group', count: 2) + expect(page).not_to have_field(with: 'Product Owner 2') + expect(page).to have_field(with: 'Product Owner 1') + expect(page).to have_field(with: 'Product Owner 3') + end + end + + scenario 'User can add and remove multiple admin users' do + within('#multiple_admin_users') do + expect(page).to have_selector('.input-group', count: 1) + find('a', text: '+ add more').click + find('a', text: '+ add more').click + + # Fill in fields with identifiable values + all('.input-group input').each_with_index do |input, index| + input.set("Admin User #{index + 1}") + end + + expect(page).to have_selector('.input-group', count: 3) + + # Delete 'Admin User 2' + all('.input-group-append')[1].click + expect(page).to have_selector('.input-group', count: 2) + expect(page).not_to have_field(with: 'Admin User 2') + expect(page).to have_field(with: 'Admin User 1') + expect(page).to have_field(with: 'Admin User 3') + end + end +end