From d067bb4f061c42e7d2ba23357e801ab8bfa97e7a Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 15:21:17 -0500 Subject: [PATCH 01/17] Started switching user data entry to use autocomplete --- app/assets/stylesheets/application.scss | 40 +++++++++++++++ app/helpers/project_helper.rb | 22 ++++++++ app/javascript/entrypoints/application.js | 3 -- app/javascript/entrypoints/user_datalist.js | 56 --------------------- app/models/user.rb | 35 ++++++++----- app/views/layouts/application.html.erb | 11 +--- app/views/projects/_data_list.html.erb | 17 ------- app/views/projects/_edit_form.html.erb | 43 ++++++++++++++-- 8 files changed, 126 insertions(+), 101 deletions(-) delete mode 100644 app/javascript/entrypoints/user_datalist.js delete mode 100644 app/views/projects/_data_list.html.erb diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 6e409c36..c8593431 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -35,3 +35,43 @@ body { } } +/* These are the styles used by the jQuery autocomplete plug-in */ +.autocomplete-suggestions { + border: 1px solid #999; + background: #FFF; + overflow: auto; +} + +.autocomplete-suggestion { + padding: 2px 5px; + white-space: nowrap; + overflow: hidden; +} + +.autocomplete-selected { + background: #F0F0F0; +} + +.autocomplete-suggestions strong { + font-weight: normal; + color: #3399FF; +} + +.autocomplete-group { + padding: 2px 5px; +} + +.autocomplete-group strong { + display: block; + border-bottom: 1px solid #000; +} + +/* + * Adds the arrow to the autocomplete textbox. + * Notice that since CSS does not allow ::after on HTML we apply this style to an HTML . + * See https://stackoverflow.com/questions/2587669/can-i-use-a-before-or-after-pseudo-element-on-an-input-field + */ +.autocomplete-arrow::after { + content: "▾"; + margin-left: -20px; +} diff --git a/app/helpers/project_helper.rb b/app/helpers/project_helper.rb index 6173b5c9..f5dc1da4 100644 --- a/app/helpers/project_helper.rb +++ b/app/helpers/project_helper.rb @@ -10,4 +10,26 @@ def pre_populate_data_sponsor @project.metadata[:data_sponsor] end end + + # Returns a string with JSON representation of a list of users + # The JSON can be used to feed the jQuery Autocomplete plug-in + def user_list_json(users) + json_elements = users.map do |user| + "{ data: '#{user.uid}', value: '#{user.display_name_safe} (#{user.uid})' }" + end + + json_elements.join(",").html_safe + end + + def sponsor_list_json + user_list_json(User.sponsor_users_list) + end + + def manager_list_json + user_list_json(User.manager_users_list) + end + + def all_users_list_json + user_list_json(User.all) + end end diff --git a/app/javascript/entrypoints/application.js b/app/javascript/entrypoints/application.js index b4b8d47a..42157063 100644 --- a/app/javascript/entrypoints/application.js +++ b/app/javascript/entrypoints/application.js @@ -16,7 +16,6 @@ import * as bootstrap from 'bootstrap'; import '../channels'; import { setTargetHtml } from './helper'; -import UserDatalist from './user_datalist'; import { displayMediafluxVersion } from './mediafluxVersion'; import { showCreateScript } from './atermScripts'; @@ -245,8 +244,6 @@ function initPage() { $('#test-jquery').click((event) => { setTargetHtml(event, 'jQuery works!'); }); - const datalist = new UserDatalist(); - datalist.setupDatalistValidity(); initDataUsers(); initListContentsModal(); showMoreLessContent(); diff --git a/app/javascript/entrypoints/user_datalist.js b/app/javascript/entrypoints/user_datalist.js deleted file mode 100644 index 2b07834f..00000000 --- a/app/javascript/entrypoints/user_datalist.js +++ /dev/null @@ -1,56 +0,0 @@ -// Enforcing a value for the datalist utilizing custom validity -// Code idea taken from https://www.jotform.com/blog/html5-datalists-what-you-need-to-know-78024/ -export default class UserDatalist { - setupDatalistValidity() { - // Find all inputs on the DOM which are bound to a datalist via their list attribute. - const inputs = document.querySelectorAll('input[list]'); - for (let i = 0; i < inputs.length; i += 1) { - // When the value of the input changes... - const element = inputs[i]; - element.addEventListener('change', this.userRoleChange); - - // This is necessary for the initial page load - this.userRoleChange.apply(element); - } - } - - userRoleChange() { - let optionFound = false; - const datalist = this.list; - // Determine whether an option exists with the current value of the input. - for (let j = 0; j < datalist.options.length; j += 1) { - if (this.value === datalist.options[j].value) { - optionFound = true; - break; - } - } - // use the setCustomValidity function of the Validation API - // to provide an user feedback if the value does not exist in the datalist - if (optionFound) { - this.setCustomValidity(''); - } else if (this.required || this.value !== '') { - this.setCustomValidity('Please select a valid value.'); - } - - const buttons = this.parentNode.querySelectorAll('[type=Submit]'); - if (this.value !== '') { - for (let x = 0; x < buttons.length; x += 1) { - buttons[x].disabled = !optionFound; - } - } else { - for (let x = 0; x < buttons.length; x += 1) { - buttons[x].disabled = true; - } - } - - if (this.required) { - const formButtons = this.form.querySelectorAll('[value=Submit]'); - for (let k = 0; k < formButtons.length; k += 1) { - formButtons[k].disabled = !optionFound; - } - - // tell the user if there are errors - this.reportValidity(); - } - } -} diff --git a/app/models/user.rb b/app/models/user.rb index 88a09972..edaaf4bf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,23 +23,32 @@ def self.all_users User.all.map(&:uid) end + # Users that can be project sponsors + def self.sponsor_users_list + if Rails.env.development? || Rails.env.staging? + User.where(eligible_sponsor: true).or(User.where(superuser: true)) + else + User.where(eligible_sponsor: true) + end + end + + # Users that can be data managers + def self.manager_users_list + if Rails.env.development? || Rails.env.staging? + User.where(eligible_manager: true).or(User.where(superuser: true)) + else + User.where(eligible_manager: true) + end + end + + # UIDs of users that can be project sponsors def self.sponsor_users - users = if Rails.env.development? || Rails.env.staging? - User.where(eligible_sponsor: true).or(User.where(superuser: true)) - else - User.where(eligible_sponsor: true) - end - users.map(&:uid) + self.sponsor_users_list.map(&:uid) end - # All users who are eligible to be Data Managers + # UIDs of users that can data managers def self.manager_users - users = if Rails.env.development? || Rails.env.staging? - User.where(eligible_manager: true).or(User.where(superuser: true)) - else - User.where(eligible_manager: true) - end - users.map(&:uid) + self.manager_users_list.map(&:uid) end def clear_mediaflux_session(session) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 095a28f0..34cf0f2a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -16,15 +16,8 @@ <%= vite_javascript_tag 'application' %> - + + <% if Rails.env.development? || Rails.env.staging? %> <%= render 'shared/plausible_dev_staging' %> diff --git a/app/views/projects/_data_list.html.erb b/app/views/projects/_data_list.html.erb deleted file mode 100644 index 5a19f454..00000000 --- a/app/views/projects/_data_list.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - <% User.all_users.each do |uid| %> - - - - - - <% User.manager_users.each do |uid| %> - \ No newline at end of file diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index 08cfd78d..ed0eca37 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -1,5 +1,4 @@ <%= render 'form_errors' %> -<%= render 'data_list' %>

Project Roles

@@ -10,12 +9,13 @@
<% if current_user.superuser? %> - + <% else %> @@ -31,13 +31,14 @@
<% if !@project.persisted? || @project.metadata_model.data_sponsor == current_user.uid %> -
<%= image_tag("custom_error.svg", alt: "This field is required") %>
This field is required.
+ <% else %> <%= @project.metadata_model.data_manager %> @@ -59,6 +60,7 @@
+
@@ -79,6 +81,7 @@
+
@@ -122,6 +125,7 @@
+
@@ -132,6 +136,8 @@
+
+
@@ -148,6 +154,7 @@
Required
+
Required
@@ -212,3 +219,33 @@
+ + + +
From 935e9de2f151c0cbe868ed09123ab15276794f48 Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 15:52:04 -0500 Subject: [PATCH 02/17] Cleaning up... --- app/helpers/project_helper.rb | 2 + app/models/user.rb | 18 +- app/views/projects/_edit_form.html.erb | 5 +- spec/fixtures/failed_tests.txt | 356 ------------------------- spec/models/user_spec.rb | 4 +- 5 files changed, 9 insertions(+), 376 deletions(-) delete mode 100644 spec/fixtures/failed_tests.txt diff --git a/app/helpers/project_helper.rb b/app/helpers/project_helper.rb index f5dc1da4..94f67644 100644 --- a/app/helpers/project_helper.rb +++ b/app/helpers/project_helper.rb @@ -13,6 +13,7 @@ def pre_populate_data_sponsor # Returns a string with JSON representation of a list of users # The JSON can be used to feed the jQuery Autocomplete plug-in + # rubocop:disable Rails/OutputSafety def user_list_json(users) json_elements = users.map do |user| "{ data: '#{user.uid}', value: '#{user.display_name_safe} (#{user.uid})' }" @@ -20,6 +21,7 @@ def user_list_json(users) json_elements.join(",").html_safe end + # rubocop:enable Rails/OutputSafety def sponsor_list_json user_list_json(User.sponsor_users_list) diff --git a/app/models/user.rb b/app/models/user.rb index edaaf4bf..2a198f55 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -19,12 +19,8 @@ def self.from_cas(access_token) user end - def self.all_users - User.all.map(&:uid) - end - # Users that can be project sponsors - def self.sponsor_users_list + def self.sponsor_users if Rails.env.development? || Rails.env.staging? User.where(eligible_sponsor: true).or(User.where(superuser: true)) else @@ -33,7 +29,7 @@ def self.sponsor_users_list end # Users that can be data managers - def self.manager_users_list + def self.manager_users if Rails.env.development? || Rails.env.staging? User.where(eligible_manager: true).or(User.where(superuser: true)) else @@ -41,16 +37,6 @@ def self.manager_users_list end end - # UIDs of users that can be project sponsors - def self.sponsor_users - self.sponsor_users_list.map(&:uid) - end - - # UIDs of users that can data managers - def self.manager_users - self.manager_users_list.map(&:uid) - end - def clear_mediaflux_session(session) @mediaflux_session = nil session[:mediaflux_session] = nil diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index ed0eca37..f362c5bc 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -221,6 +221,9 @@
- -
diff --git a/spec/fixtures/failed_tests.txt b/spec/fixtures/failed_tests.txt deleted file mode 100644 index 1b06d22a..00000000 --- a/spec/fixtures/failed_tests.txt +++ /dev/null @@ -1,356 +0,0 @@ -example_id | status | run_time | ----------------------------------------------------------------------------- | ------- | --------------- | -./spec/channels/mediaflux_channel_spec.rb[1:1:1] | passed | 0.04446 seconds | -./spec/channels/mediaflux_channel_spec.rb[1:2:1] | passed | 0.12407 seconds | -./spec/channels/mediaflux_channel_spec.rb[1:3:1:1] | passed | 0.04985 seconds | -./spec/controllers/mediaflux_info_controller_spec.rb[1:1] | passed | 0.03834 seconds | -./spec/controllers/mediaflux_info_controller_spec.rb[1:2] | passed | 0.06742 seconds | -./spec/controllers/omniauth_callbacks_controller_spec.rb[1:1:1] | passed | 0.00504 seconds | -./spec/controllers/omniauth_callbacks_controller_spec.rb[1:2:1] | passed | 0.00358 seconds | -./spec/controllers/omniauth_callbacks_controller_spec.rb[1:3:1] | passed | 0.00248 seconds | -./spec/controllers/projects_controller_spec.rb[1:1:1] | passed | 0.02412 seconds | -./spec/controllers/projects_controller_spec.rb[1:1:2:1] | passed | 0.02989 seconds | -./spec/controllers/projects_controller_spec.rb[1:1:2:2] | passed | 0.02768 seconds | -./spec/controllers/projects_controller_spec.rb[1:2:1:1] | passed | 0.02712 seconds | -./spec/controllers/welcome_controller_spec.rb[1:1] | passed | 0.00535 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:1] | passed | 0.03599 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:2] | passed | 0.04414 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:3:1] | passed | 0.045 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:4:1] | passed | 0.04197 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:5:1] | passed | 0.03014 seconds | -./spec/controllers/welcome_controller_spec.rb[1:2:6:1] | passed | 0.03614 seconds | -./spec/controllers/welcome_controller_spec.rb[1:3] | passed | 0.01111 seconds | -./spec/controllers/welcome_controller_spec.rb[1:4:1] | passed | 0.0355 seconds | -./spec/jobs/activate_project_job_spec.rb[1:1:1] | passed | 0.18751 seconds | -./spec/jobs/file_inventory_cleanup_job_spec.rb[1:1:1] | passed | 0.10138 seconds | -./spec/jobs/file_inventory_cleanup_job_spec.rb[1:1:2] | passed | 0.10716 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:1:1] | passed | 0.08569 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:1] | passed | 0.10233 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:2] | passed | 0.09102 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:3] | passed | 0.09525 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:4] | passed | 0.09743 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:5] | passed | 0.09404 seconds | -./spec/jobs/file_inventory_job_spec.rb[1:2:6:1] | passed | 0.074 seconds | -./spec/lib/xml_utilities_spec.rb[1:1] | passed | 0.00182 seconds | -./spec/lib/xml_utilities_spec.rb[1:2] | passed | 0.00138 seconds | -./spec/lib/xml_utilities_spec.rb[1:3] | passed | 0.00189 seconds | -./spec/mailers/tigerdata_spec.rb[1:1] | passed | 0.09127 seconds | -./spec/mailers/tigerdata_spec.rb[1:2] | passed | 0.05307 seconds | -./spec/mailers/tigerdata_spec.rb[1:3:1] | passed | 0.00318 seconds | -./spec/models/breadcrumb_spec.rb[1:1] | pending | 0.00001 seconds | -./spec/models/file_inventory_request_spec.rb[1:1:1] | passed | 0.06366 seconds | -./spec/models/file_inventory_request_spec.rb[1:2:1] | passed | 0.02609 seconds | -./spec/models/mediaflux/accumulator_create_collection_request_spec.rb[1:1:1] | passed | 0.11762 seconds | -./spec/models/mediaflux/asset_create_request_spec.rb[1:1:1] | passed | 0.04425 seconds | -./spec/models/mediaflux/asset_create_request_spec.rb[1:1:2:1] | passed | 0.10596 seconds | -./spec/models/mediaflux/asset_create_request_spec.rb[1:2:1] | passed | 0.02525 seconds | -./spec/models/mediaflux/asset_create_request_spec.rb[1:3:1] | passed | 0.02635 seconds | -./spec/models/mediaflux/asset_destroy_request_spec.rb[1:1:1] | passed | 0.07297 seconds | -./spec/models/mediaflux/asset_exist_request_headers_spec.rb[1:1:1] | passed | 0.03449 seconds | -./spec/models/mediaflux/asset_exist_request_headers_spec.rb[1:2:1] | passed | 0.03524 seconds | -./spec/models/mediaflux/asset_exist_request_spec.rb[1:1:1] | passed | 0.04002 seconds | -./spec/models/mediaflux/asset_metadata_request_spec.rb[1:1:1] | passed | 0.14059 seconds | -./spec/models/mediaflux/asset_metadata_request_spec.rb[1:1:2:1] | passed | 0.09239 seconds | -./spec/models/mediaflux/asset_metadata_request_spec.rb[1:1:3:1] | passed | 0.13602 seconds | -./spec/models/mediaflux/asset_spec.rb[1:1:1] | passed | 0.00285 seconds | -./spec/models/mediaflux/asset_spec.rb[1:2:1] | passed | 0.00304 seconds | -./spec/models/mediaflux/asset_spec.rb[1:3:1] | passed | 0.00442 seconds | -./spec/models/mediaflux/asset_spec.rb[1:4:1] | passed | 0.0052 seconds | -./spec/models/mediaflux/collection_asset_create_root_request_spec.rb[1:1] | passed | 0.24514 seconds | -./spec/models/mediaflux/collection_list_request_spec.rb[1:1:1] | passed | 0.05448 seconds | -./spec/models/mediaflux/connection_spec.rb[1:1:1] | passed | 0.00135 seconds | -./spec/models/mediaflux/connection_spec.rb[1:1:2:1] | passed | 0.00128 seconds | -./spec/models/mediaflux/connection_spec.rb[1:2:1] | passed | 0.00507 seconds | -./spec/models/mediaflux/connection_spec.rb[1:2:2:1] | passed | 0.00238 seconds | -./spec/models/mediaflux/connection_spec.rb[1:3:1] | passed | 0.00211 seconds | -./spec/models/mediaflux/connection_spec.rb[1:3:2:1] | passed | 0.00278 seconds | -./spec/models/mediaflux/connection_spec.rb[1:4:1] | passed | 0.00262 seconds | -./spec/models/mediaflux/connection_spec.rb[1:4:2:1] | passed | 0.00273 seconds | -./spec/models/mediaflux/connection_spec.rb[1:5:1] | passed | 0.00127 seconds | -./spec/models/mediaflux/connection_spec.rb[1:5:2:1] | passed | 0.0013 seconds | -./spec/models/mediaflux/connection_spec.rb[1:6:1] | passed | 0.00256 seconds | -./spec/models/mediaflux/connection_spec.rb[1:6:2:1] | passed | 0.00274 seconds | -./spec/models/mediaflux/connection_spec.rb[1:7:1] | passed | 0.00303 seconds | -./spec/models/mediaflux/connection_spec.rb[1:7:2:1] | passed | 0.00174 seconds | -./spec/models/mediaflux/http_connection_spec.rb[1:1:1] | passed | 0.00183 seconds | -./spec/models/mediaflux/http_connection_spec.rb[1:1:2] | passed | 0.00706 seconds | -./spec/models/mediaflux/iterator_request_spec.rb[1:1:1] | passed | 0.1147 seconds | -./spec/models/mediaflux/iterator_request_spec.rb[1:2:1] | passed | 0.13746 seconds | -./spec/models/mediaflux/iterator_request_spec.rb[1:3:1] | passed | 0.10145 seconds | -./spec/models/mediaflux/logoff_request_spec.rb[1:1:1] | passed | 0.03066 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:1:1] | passed | 0.03578 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:1:2:1] | passed | 0.03044 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:1:3:1] | passed | 0.03999 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:1:4:1] | passed | 0.03463 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:1:5:1] | passed | 0.03327 seconds | -./spec/models/mediaflux/logon_request_spec.rb[1:2:1] | passed | 0.02926 seconds | -./spec/models/mediaflux/namespace_create_request_spec.rb[1:1:1] | passed | 0.04616 seconds | -./spec/models/mediaflux/namespace_describe_request_spec.rb[1:1:1] | passed | 0.03207 seconds | -./spec/models/mediaflux/namespace_describe_request_spec.rb[1:1:2] | passed | 0.03522 seconds | -./spec/models/mediaflux/namespace_destroy_request_spec.rb[1:1:1] | passed | 0.17013 seconds | -./spec/models/mediaflux/namespace_exist_request_spec.rb[1:1:1] | passed | 0.037 seconds | -./spec/models/mediaflux/namespace_list_request_spec.rb[1:1:1] | passed | 0.05354 seconds | -./spec/models/mediaflux/project_approve_request_spec.rb[1:1:1] | passed | 0.17032 seconds | -./spec/models/mediaflux/project_create_request_spec.rb[1:1:1] | passed | 0.09572 seconds | -./spec/models/mediaflux/project_create_request_spec.rb[1:2:1] | passed | 0.0458 seconds | -./spec/models/mediaflux/project_create_request_spec.rb[1:3:1] | passed | 0.08393 seconds | -./spec/models/mediaflux/project_update_request_spec.rb[1:1:1] | passed | 0.12197 seconds | -./spec/models/mediaflux/project_update_request_spec.rb[1:1:2:1] | passed | 0.16626 seconds | -./spec/models/mediaflux/project_update_request_spec.rb[1:2:1] | passed | 0.06329 seconds | -./spec/models/mediaflux/query_request_spec.rb[1:1:1] | passed | 0.1058 seconds | -./spec/models/mediaflux/query_request_spec.rb[1:2:1] | passed | 0.11089 seconds | -./spec/models/mediaflux/query_request_spec.rb[1:3:1] | passed | 0.11071 seconds | -./spec/models/mediaflux/query_request_spec.rb[1:4:1] | passed | 0.11625 seconds | -./spec/models/mediaflux/range_query_request_spec.rb[1:1:1] | passed | 0.59882 seconds | -./spec/models/mediaflux/range_query_request_spec.rb[1:2:1] | passed | 0.41098 seconds | -./spec/models/mediaflux/request_spec.rb[1:1:1] | passed | 0.03352 seconds | -./spec/models/mediaflux/request_spec.rb[1:1:2:1:1:1] | passed | 0.04446 seconds | -./spec/models/mediaflux/root_collection_asset_spec.rb[1:1:1] | passed | 0.04396 seconds | -./spec/models/mediaflux/root_collection_asset_spec.rb[1:1:2] | passed | 0.05906 seconds | -./spec/models/mediaflux/schema_create_request_spec.rb[1:1:1] | passed | 0.03813 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:1:1] | passed | 0.25062 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:2:1] | passed | 0.27798 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:3:1] | passed | 0.53736 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:4:1] | passed | 0.31397 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:5:1] | passed | 0.25384 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:6:1] | passed | 0.35062 seconds | -./spec/models/mediaflux/schema_fields_create_request_spec.rb[1:7:1] | passed | 0.37135 seconds | -./spec/models/mediaflux/service_execute_request_spec.rb[1:1:1] | passed | 0.07903 seconds | -./spec/models/mediaflux/service_execute_request_spec.rb[1:1:2:1] | passed | 0.07467 seconds | -./spec/models/mediaflux/service_execute_request_spec.rb[1:1:3:1] | passed | 0.10158 seconds | -./spec/models/mediaflux/store_list_request_spec.rb[1:1:1] | passed | 0.09645 seconds | -./spec/models/mediaflux/test_asset_create_request_spec.rb[1:1:1] | passed | 0.13456 seconds | -./spec/models/mediaflux/time_spec.rb[1:1:1] | passed | 0.09 seconds | -./spec/models/mediaflux/time_spec.rb[1:2:1:1] | passed | 0.01819 seconds | -./spec/models/mediaflux/token_create_request_spec.rb[1:1:1] | passed | 0.06165 seconds | -./spec/models/mediaflux/version_request_spec.rb[1:1:1] | passed | 0.03274 seconds | -./spec/models/mediaflux_status_spec.rb[1:1:1] | passed | 0.03016 seconds | -./spec/models/mediaflux_status_spec.rb[1:2:1] | passed | 0.02431 seconds | -./spec/models/project_accumulator_spec.rb[1:1:1] | passed | 0.09591 seconds | -./spec/models/project_accumulator_spec.rb[1:1:2] | passed | 0.07537 seconds | -./spec/models/project_accumulator_spec.rb[1:2:1] | passed | 0.08582 seconds | -./spec/models/project_accumulator_spec.rb[1:2:2] | passed | 0.1045 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:1] | passed | 0.13684 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:2:1] | passed | 0.12795 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:3:1] | passed | 0.11187 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:4:1] | passed | 0.12754 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:4:2] | passed | 0.12955 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:1:4:3] | passed | 0.07858 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:2:1] | passed | 0.14266 seconds | -./spec/models/project_mediaflux_spec.rb[1:1:2:2] | passed | 0.13577 seconds | -./spec/models/project_mediaflux_spec.rb[1:2:1] | passed | 0.14076 seconds | -./spec/models/project_mediaflux_spec.rb[1:2:2] | passed | 0.15379 seconds | -./spec/models/project_metadata_spec.rb[1:1:1] | passed | 0.00172 seconds | -./spec/models/project_metadata_spec.rb[1:1:2] | passed | 0.00146 seconds | -./spec/models/project_metadata_spec.rb[1:1:3] | passed | 0.00258 seconds | -./spec/models/project_metadata_spec.rb[1:2:1] | passed | 0.00155 seconds | -./spec/models/project_metadata_spec.rb[1:2:2] | passed | 0.00193 seconds | -./spec/models/project_metadata_spec.rb[1:2:3] | passed | 0.00129 seconds | -./spec/models/project_metadata_spec.rb[1:2:4] | passed | 0.00123 seconds | -./spec/models/project_metadata_spec.rb[1:2:5] | passed | 0.00119 seconds | -./spec/models/project_metadata_spec.rb[1:3:1] | passed | 0.00401 seconds | -./spec/models/project_show_presenter_spec.rb[1:1:1] | passed | 0.01999 seconds | -./spec/models/project_show_presenter_spec.rb[1:2:1] | passed | 0.0198 seconds | -./spec/models/project_show_presenter_spec.rb[1:3:1] | passed | 0.02181 seconds | -./spec/models/project_show_presenter_spec.rb[1:4:1] | passed | 0.02075 seconds | -./spec/models/project_show_presenter_spec.rb[1:5:1] | passed | 0.01955 seconds | -./spec/models/project_show_presenter_spec.rb[1:6:1] | passed | 0.02184 seconds | -./spec/models/project_show_presenter_spec.rb[1:7:1] | passed | 0.02199 seconds | -./spec/models/project_show_presenter_spec.rb[1:8:1] | passed | 0.02121 seconds | -./spec/models/project_show_presenter_spec.rb[1:9:1] | passed | 0.02564 seconds | -./spec/models/project_show_presenter_spec.rb[1:10:1] | passed | 0.01995 seconds | -./spec/models/project_show_presenter_spec.rb[1:11:1] | passed | 0.01984 seconds | -./spec/models/project_show_presenter_spec.rb[1:12:1] | passed | 0.02165 seconds | -./spec/models/project_spec.rb[1:1:1] | passed | 0.08239 seconds | -./spec/models/project_spec.rb[1:2:1] | passed | 0.09242 seconds | -./spec/models/project_spec.rb[1:3:1] | passed | 0.11734 seconds | -./spec/models/project_spec.rb[1:4:1] | passed | 0.08904 seconds | -./spec/models/project_spec.rb[1:5:1] | passed | 0.0778 seconds | -./spec/models/project_spec.rb[1:6:1] | passed | 0.04925 seconds | -./spec/models/project_spec.rb[1:6:2] | passed | 0.05996 seconds | -./spec/models/project_spec.rb[1:7:1] | passed | 0.16008 seconds | -./spec/models/project_spec.rb[1:8:1] | passed | 0.05177 seconds | -./spec/models/project_spec.rb[1:9:1] | passed | 0.07704 seconds | -./spec/models/project_spec.rb[1:9:2:1] | passed | 0.13701 seconds | -./spec/models/project_spec.rb[1:10:1] | passed | 0.0411 seconds | -./spec/models/project_spec.rb[1:10:2] | passed | 0.05212 seconds | -./spec/models/project_spec.rb[1:10:3] | passed | 0.08742 seconds | -./spec/models/project_spec.rb[1:11:1] | passed | 0.04424 seconds | -./spec/models/project_spec.rb[1:12:1] | passed | 0.03564 seconds | -./spec/models/project_spec.rb[1:12:2] | passed | 0.04051 seconds | -./spec/models/project_spec.rb[1:13:1] | passed | 0.04463 seconds | -./spec/models/project_spec.rb[1:14:1:1] | passed | 0.06193 seconds | -./spec/models/project_spec.rb[1:14:1:2] | passed | 0.0566 seconds | -./spec/models/project_spec.rb[1:14:1:3] | passed | 0.05545 seconds | -./spec/models/project_spec.rb[1:14:2:1] | passed | 0.08151 seconds | -./spec/models/project_spec.rb[1:15:1] | passed | 0.0653 seconds | -./spec/models/project_spec.rb[1:15:2] | passed | 0.07568 seconds | -./spec/models/project_spec.rb[1:16:1] | passed | 0.1364 seconds | -./spec/models/project_spec.rb[1:16:2] | passed | 0.1949 seconds | -./spec/models/project_validator_spec.rb[1:1:1:1] | passed | 0.02407 seconds | -./spec/models/project_validator_spec.rb[1:1:2:1] | passed | 0.01435 seconds | -./spec/models/project_validator_spec.rb[1:1:3:1] | passed | 0.01394 seconds | -./spec/models/project_validator_spec.rb[1:1:4:1] | passed | 0.01465 seconds | -./spec/models/project_validator_spec.rb[1:1:5:1] | passed | 0.01288 seconds | -./spec/models/project_validator_spec.rb[1:1:6:1] | passed | 0.0189 seconds | -./spec/models/project_validator_spec.rb[1:1:7:1] | passed | 0.01735 seconds | -./spec/models/provenance_event_spec.rb[1:1:1] | passed | 0.00174 seconds | -./spec/models/provenance_event_spec.rb[1:1:2] | passed | 0.00184 seconds | -./spec/models/provenance_event_spec.rb[1:2:1] | passed | 0.00163 seconds | -./spec/models/provenance_event_spec.rb[1:3:1] | passed | 0.00184 seconds | -./spec/models/provenance_event_spec.rb[1:4:1] | passed | 0.00162 seconds | -./spec/models/tigerdata_schema_spec.rb[1:1:1] | passed | 0.00218 seconds | -./spec/models/tigerdata_schema_spec.rb[1:1:2] | passed | 0.00198 seconds | -./spec/models/tigerdata_schema_spec.rb[1:2:1] | passed | 0.00339 seconds | -./spec/models/user_request_spec.rb[1:1:1] | passed | 0.02876 seconds | -./spec/models/user_request_spec.rb[1:2:1] | passed | 0.034 seconds | -./spec/models/user_request_spec.rb[1:3:1] | passed | 0.03246 seconds | -./spec/models/user_request_spec.rb[1:4:1] | passed | 0.02964 seconds | -./spec/models/user_request_spec.rb[1:5:1] | passed | 0.03323 seconds | -./spec/models/user_request_spec.rb[1:6:1] | passed | 0.04361 seconds | -./spec/models/user_request_spec.rb[1:6:2] | passed | 0.02975 seconds | -./spec/models/user_request_spec.rb[1:6:3] | passed | 0.04926 seconds | -./spec/models/user_request_spec.rb[1:6:4] | passed | 0.04139 seconds | -./spec/models/user_request_spec.rb[1:6:5] | passed | 0.03277 seconds | -./spec/models/user_request_spec.rb[1:7:1] | passed | 0.04829 seconds | -./spec/models/user_request_spec.rb[1:8:1] | passed | 0.04506 seconds | -./spec/models/user_spec.rb[1:1:1] | passed | 0.00219 seconds | -./spec/models/user_spec.rb[1:1:2] | passed | 0.00469 seconds | -./spec/models/user_spec.rb[1:1:3] | passed | 0.0066 seconds | -./spec/models/user_spec.rb[1:2:1] | passed | 0.00714 seconds | -./spec/models/user_spec.rb[1:2:2] | passed | 0.00766 seconds | -./spec/models/user_spec.rb[1:2:3] | passed | 0.00792 seconds | -./spec/models/user_spec.rb[1:2:4] | passed | 0.00841 seconds | -./spec/models/user_spec.rb[1:2:5] | passed | 0.00701 seconds | -./spec/models/user_spec.rb[1:2:6] | passed | 0.00705 seconds | -./spec/models/user_spec.rb[1:3:1] | passed | 0.25272 seconds | -./spec/models/user_spec.rb[1:3:2] | passed | 0.25599 seconds | -./spec/models/user_spec.rb[1:3:3] | passed | 0.27702 seconds | -./spec/models/user_spec.rb[1:3:4] | passed | 0.1917 seconds | -./spec/models/user_spec.rb[1:3:5] | passed | 0.25848 seconds | -./spec/models/user_spec.rb[1:3:6] | passed | 0.27578 seconds | -./spec/models/user_spec.rb[1:4:1] | passed | 0.00898 seconds | -./spec/models/user_spec.rb[1:5:1] | passed | 0.0101 seconds | -./spec/models/user_spec.rb[1:5:2] | passed | 0.0111 seconds | -./spec/models/user_spec.rb[1:6:1] | passed | 0.00418 seconds | -./spec/models/user_spec.rb[1:6:2] | passed | 0.00375 seconds | -./spec/models/user_spec.rb[1:7:1] | passed | 0.0123 seconds | -./spec/models/user_spec.rb[1:7:2:1] | passed | 0.01574 seconds | -./spec/models/user_spec.rb[1:8:1] | passed | 0.00366 seconds | -./spec/models/user_spec.rb[1:8:2] | passed | 0.00387 seconds | -./spec/models/version_footer_spec.rb[1:1:1:1] | passed | 0.05027 seconds | -./spec/models/version_footer_spec.rb[1:1:2:1] | passed | 0.04074 seconds | -./spec/models/version_footer_spec.rb[1:2:1:1] | passed | 0.00166 seconds | -./spec/models/version_footer_spec.rb[1:2:2:1] | passed | 0.0202 seconds | -./spec/models/version_footer_spec.rb[1:3:1:1] | passed | 0.00166 seconds | -./spec/models/version_footer_spec.rb[1:4:1:1] | passed | 0.03917 seconds | -./spec/models/version_footer_spec.rb[1:4:1:2:1] | passed | 0.002 seconds | -./spec/models/version_footer_spec.rb[1:5:1:1] | passed | 0.0392 seconds | -./spec/models/version_footer_spec.rb[1:5:1:2:1] | passed | 0.00235 seconds | -./spec/models/version_footer_spec.rb[1:6:1] | passed | 0.08248 seconds | -./spec/models/version_footer_spec.rb[1:7:1] | passed | 0.00342 seconds | -./spec/requests/mediaflux_info_spec.rb[1:1:1] | passed | 0.04484 seconds | -./spec/requests/mediaflux_info_spec.rb[1:1:2:1] | passed | 0.04906 seconds | -./spec/requests/projects_spec.rb[1:1:1] | passed | 0.03248 seconds | -./spec/requests/projects_spec.rb[1:1:2:1] | passed | 0.04896 seconds | -./spec/requests/projects_spec.rb[1:1:2:2] | passed | 0.06476 seconds | -./spec/requests/projects_spec.rb[1:1:2:3] | passed | 0.0537 seconds | -./spec/requests/projects_spec.rb[1:1:2:4:1] | passed | 0.04277 seconds | -./spec/requests/projects_spec.rb[1:1:2:5:1] | passed | 0.08067 seconds | -./spec/routing/mediaflux_infos_routing_spec.rb[1:1:1] | passed | 0.00198 seconds | -./spec/services/mediaflux_script_factory_spec.rb[1:1:1] | passed | 0.02364 seconds | -./spec/services/pul_datacite_spec.rb[1:1:1] | passed | 0.00326 seconds | -./spec/services/pul_datacite_spec.rb[1:1:2:1] | passed | 0.00203 seconds | -./spec/services/pul_datacite_spec.rb[1:1:2:2:1] | passed | 0.00192 seconds | -./spec/services/test_asset_generator_spec.rb[1:1:1] | passed | 0.03068 seconds | -./spec/services/test_asset_generator_spec.rb[1:1:2:1] | passed | 0.03234 seconds | -./spec/services/test_asset_generator_spec.rb[1:1:3:1] | passed | 0.03173 seconds | -./spec/services/test_asset_generator_spec.rb[1:1:4:1] | passed | 0.03012 seconds | -./spec/services/test_project_generator_spec.rb[1:1:1] | passed | 0.36008 seconds | -./spec/system/accessibility_spec.rb[1:1:1] | passed | 0.34415 seconds | -./spec/system/accessibility_spec.rb[1:2:1] | passed | 0.3531 seconds | -./spec/system/accessibility_spec.rb[1:3:1] | passed | 0.44209 seconds | -./spec/system/accessibility_spec.rb[1:4:1] | passed | 0.34668 seconds | -./spec/system/banner_spec.rb[1:1] | passed | 0.08251 seconds | -./spec/system/emulator_spec.rb[1:1] | passed | 0.26347 seconds | -./spec/system/features_spec.rb[1:1] | passed | 20.44 seconds | -./spec/system/features_spec.rb[1:2:1] | passed | 0.04021 seconds | -./spec/system/features_spec.rb[1:3:1] | passed | 0.03917 seconds | -./spec/system/features_spec.rb[1:4:1] | passed | 0.07604 seconds | -./spec/system/features_spec.rb[1:5:1] | passed | 0.06314 seconds | -./spec/system/mediaflux_info_spec.rb[1:1] | passed | 0.16354 seconds | -./spec/system/mediaflux_session_spec.rb[1:1] | passed | 0.20147 seconds | -./spec/system/project_roles_spec.rb[1:1] | passed | 5.85 seconds | -./spec/system/project_roles_spec.rb[1:2:1] | passed | 0.25482 seconds | -./spec/system/project_roles_spec.rb[1:2:2] | passed | 0.24343 seconds | -./spec/system/project_roles_spec.rb[1:2:3] | passed | 0.16744 seconds | -./spec/system/project_roles_spec.rb[1:2:4] | passed | 0.18284 seconds | -./spec/system/project_roles_spec.rb[1:2:5] | passed | 0.15549 seconds | -./spec/system/project_roles_spec.rb[1:2:6] | passed | 0.15838 seconds | -./spec/system/project_roles_spec.rb[1:3:1] | passed | 0.19985 seconds | -./spec/system/project_roles_spec.rb[1:4:1] | passed | 0.62809 seconds | -./spec/system/project_roles_spec.rb[1:5:1] | passed | 0.60688 seconds | -./spec/system/project_roles_spec.rb[1:5:2] | passed | 0.19042 seconds | -./spec/system/project_roles_spec.rb[1:6:1] | passed | 0.08389 seconds | -./spec/system/project_roles_spec.rb[1:7:1] | passed | 1.9 seconds | -./spec/system/project_roles_spec.rb[1:7:2] | passed | 1.91 seconds | -./spec/system/project_roles_spec.rb[1:8:1] | passed | 0.41572 seconds | -./spec/system/project_roles_spec.rb[1:8:2] | passed | 0.42098 seconds | -./spec/system/project_roles_spec.rb[1:8:3] | passed | 0.17346 seconds | -./spec/system/project_roles_spec.rb[1:8:4] | passed | 0.16901 seconds | -./spec/system/project_roles_spec.rb[1:8:5] | passed | 0.19408 seconds | -./spec/system/project_show_spec.rb[1:1:1:1:1:1] | passed | 0.33851 seconds | -./spec/system/project_show_spec.rb[1:1:1:1:2:1] | passed | 0.14841 seconds | -./spec/system/project_show_spec.rb[1:1:1:2:1:1] | passed | 0.35461 seconds | -./spec/system/project_show_spec.rb[1:1:1:2:2:1] | passed | 0.17713 seconds | -./spec/system/project_show_spec.rb[1:1:2:1] | passed | 0.20144 seconds | -./spec/system/project_show_spec.rb[1:1:3:1] | passed | 0.24374 seconds | -./spec/system/project_show_spec.rb[1:1:3:2] | passed | 0.122 seconds | -./spec/system/project_show_spec.rb[1:1:4:1] | passed | 0.77722 seconds | -./spec/system/project_show_spec.rb[1:1:4:2] | passed | 0.40556 seconds | -./spec/system/project_show_spec.rb[1:1:4:3] | passed | 0.79392 seconds | -./spec/system/project_show_spec.rb[1:1:5:1] | passed | 0.24641 seconds | -./spec/system/project_show_spec.rb[1:1:5:2] | passed | 0.18666 seconds | -./spec/system/project_show_spec.rb[1:1:5:3] | passed | 0.17346 seconds | -./spec/system/project_spec.rb[1:1:1:1] | passed | 0.20754 seconds | -./spec/system/project_spec.rb[1:1:2:1] | passed | 0.53288 seconds | -./spec/system/project_spec.rb[1:2:1:1] | passed | 0.20099 seconds | -./spec/system/project_spec.rb[1:2:2:1] | passed | 0.3097 seconds | -./spec/system/project_spec.rb[1:2:2:2] | passed | 0.29282 seconds | -./spec/system/project_spec.rb[1:2:2:3] | passed | 0.20249 seconds | -./spec/system/project_spec.rb[1:2:2:4] | passed | 0.6724 seconds | -./spec/system/project_spec.rb[1:2:3:1] | passed | 0.37994 seconds | -./spec/system/project_spec.rb[1:3:1] | passed | 6.18 seconds | -./spec/system/project_spec.rb[1:3:2:1] | passed | 1.52 seconds | -./spec/system/project_spec.rb[1:3:3:1] | passed | 1.23 seconds | -./spec/system/project_spec.rb[1:3:4:1] | passed | 1.06 seconds | -./spec/system/project_spec.rb[1:3:5:1] | passed | 2.39 seconds | -./spec/system/project_spec.rb[1:3:6:1] | passed | 1.25 seconds | -./spec/system/project_spec.rb[1:3:7:1] | passed | 5.87 seconds | -./spec/system/project_spec.rb[1:3:8:1] | passed | 5.15 seconds | -./spec/system/project_spec.rb[1:4:1] | passed | 0.24122 seconds | -./spec/system/project_spec.rb[1:5:1] | passed | 0.85905 seconds | -./spec/system/project_spec.rb[1:5:2] | passed | 1.14 seconds | -./spec/system/project_spec.rb[1:6:1:1:1] | passed | 2.04 seconds | -./spec/system/project_spec.rb[1:6:1:2:1] | pending | 2.29 seconds | -./spec/system/project_spec.rb[1:6:1:3:1] | passed | 0.8759 seconds | -./spec/system/welcome_spec.rb[1:1:1] | passed | 0.13947 seconds | -./spec/system/welcome_spec.rb[1:1:2] | passed | 0.14793 seconds | -./spec/system/welcome_spec.rb[1:2:1:1] | passed | 0.246 seconds | -./spec/system/welcome_spec.rb[1:2:1:2] | passed | 1.25 seconds | -./spec/system/welcome_spec.rb[1:2:1:3] | passed | 0.34326 seconds | -./spec/system/welcome_spec.rb[1:2:1:4:1] | passed | 0.51894 seconds | -./spec/system/welcome_spec.rb[1:2:1:5:1] | passed | 0.3413 seconds | -./spec/system/welcome_spec.rb[1:2:1:6] | passed | 0.30882 seconds | -./spec/system/welcome_spec.rb[1:2:2:1] | passed | 0.28003 seconds | -./spec/system/welcome_spec.rb[1:2:2:2:1] | passed | 0.28623 seconds | -./spec/system/welcome_spec.rb[1:2:2:3:1] | passed | 0.23011 seconds | -./spec/system/welcome_spec.rb[1:2:3:1] | passed | 0.2457 seconds | -./spec/system/welcome_spec.rb[1:2:3:2] | passed | 0.25335 seconds | -./spec/system/welcome_spec.rb[1:2:4:1] | passed | 0.23054 seconds | -./spec/system/welcome_spec.rb[1:2:4:2] | passed | 0.36748 seconds | -./spec/system/welcome_spec.rb[1:2:4:3] | passed | 0.56857 seconds | -./spec/system/welcome_spec.rb[1:2:5:1] | passed | 0.31601 seconds | -./spec/system/welcome_spec.rb[1:2:5:2] | passed | 0.25346 seconds | -./spec/views/mediaflux_infos/index.html.erb_spec.rb[1:1] | passed | 0.00301 seconds | -./spec/views/shared/footer.html.erb_spec.rb[1:1:1] | passed | 0.08593 seconds | diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c76911c8..53cf49a3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -189,7 +189,7 @@ FactoryBot.create(:superuser) project_sponsor = FactoryBot.create(:project_sponsor) FactoryBot.create(:user) - expect(User.sponsor_users).to eq([project_sponsor.uid]) + expect(User.sponsor_users.map(&:uid)).to eq([project_sponsor.uid]) end context "in development" do @@ -198,7 +198,7 @@ project_sponsor = FactoryBot.create(:project_sponsor) FactoryBot.create(:user) allow(Rails.env).to receive(:development?).and_return true - expect(User.sponsor_users).to eq([superuser.uid, project_sponsor.uid]) + expect(User.sponsor_users.map(&:uid).sort).to eq([superuser.uid, project_sponsor.uid].sort) end end end From 20e595b8808f8980acd7d705377276c25d3d63ad Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 16:06:11 -0500 Subject: [PATCH 03/17] Include our local copy of the jQuery autocomplete plugin: --- app/helpers/project_helper.rb | 4 +- public/jquery.autocomplete.js | 1044 +++++++++++++++++++++++++++++++++ 2 files changed, 1046 insertions(+), 2 deletions(-) create mode 100644 public/jquery.autocomplete.js diff --git a/app/helpers/project_helper.rb b/app/helpers/project_helper.rb index 94f67644..e83f8222 100644 --- a/app/helpers/project_helper.rb +++ b/app/helpers/project_helper.rb @@ -24,11 +24,11 @@ def user_list_json(users) # rubocop:enable Rails/OutputSafety def sponsor_list_json - user_list_json(User.sponsor_users_list) + user_list_json(User.sponsor_users) end def manager_list_json - user_list_json(User.manager_users_list) + user_list_json(User.manager_users) end def all_users_list_json diff --git a/public/jquery.autocomplete.js b/public/jquery.autocomplete.js new file mode 100644 index 00000000..f71de1ce --- /dev/null +++ b/public/jquery.autocomplete.js @@ -0,0 +1,1044 @@ +/** + * Ajax Autocomplete for jQuery, version %version% + * (c) 2017 Tomas Kirda + * + * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license. + * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete + */ + +// Expose plugin as an AMD module if AMD loader is present: +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object' && typeof require === 'function') { + // Browserify + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +})( + /** + * @param {jQuery} $ + */ + function ($) { + 'use strict'; + + var utils = { + escapeRegExChars: function (value) { + return value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'); + }, + createNode: function (containerClass) { + var div = document.createElement('div'); + div.className = containerClass; + div.style.position = 'absolute'; + div.style.display = 'none'; + return div; + }, + }, + keys = { + ESC: 27, + TAB: 9, + RETURN: 13, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + }, + noop = $.noop; + + function Autocomplete(el, options) { + var that = this; + + // Shared variables: + that.element = el; + that.el = $(el); + that.suggestions = []; + that.badQueries = []; + that.selectedIndex = -1; + that.currentValue = that.element.value; + that.timeoutId = null; + that.cachedResponse = {}; + that.onChangeTimeout = null; + that.onChange = null; + that.isLocal = false; + that.suggestionsContainer = null; + that.noSuggestionsContainer = null; + that.options = $.extend(true, {}, Autocomplete.defaults, options); + that.classes = { + selected: 'autocomplete-selected', + suggestion: 'autocomplete-suggestion', + }; + that.hint = null; + that.hintValue = ''; + that.selection = null; + + // Initialize and set options: + that.initialize(); + that.setOptions(options); + } + + Autocomplete.utils = utils; + + $.Autocomplete = Autocomplete; + + Autocomplete.defaults = { + ajaxSettings: {}, + autoSelectFirst: false, + appendTo: 'body', + serviceUrl: null, + lookup: null, + onSelect: null, + onHint: null, + width: 'auto', + minChars: 1, + maxHeight: 300, + deferRequestBy: 0, + params: {}, + formatResult: _formatResult, + formatGroup: _formatGroup, + delimiter: null, + zIndex: 9999, + type: 'GET', + noCache: false, + onSearchStart: noop, + onSearchComplete: noop, + onSearchError: noop, + preserveInput: false, + containerClass: 'autocomplete-suggestions', + tabDisabled: false, + dataType: 'text', + currentRequest: null, + triggerSelectOnValidInput: true, + preventBadQueries: true, + lookupFilter: _lookupFilter, + paramName: 'query', + transformResult: _transformResult, + showNoSuggestionNotice: false, + noSuggestionNotice: 'No results', + orientation: 'bottom', + forceFixPosition: false, + }; + + function _lookupFilter(suggestion, originalQuery, queryLowerCase) { + return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1; + } + + function _transformResult(response) { + return typeof response === 'string' ? $.parseJSON(response) : response; + } + + function _formatResult(suggestion, currentValue) { + // Do not replace anything if the current value is empty + if (!currentValue) { + return suggestion.value; + } + + var pattern = '(' + utils.escapeRegExChars(currentValue) + ')'; + + return suggestion.value + .replace(new RegExp(pattern, 'gi'), '$1') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/<(\/?strong)>/g, '<$1>'); + } + + function _formatGroup(suggestion, category) { + return '
' + category + '
'; + } + + Autocomplete.prototype = { + initialize: function () { + var that = this, + suggestionSelector = '.' + that.classes.suggestion, + selected = that.classes.selected, + options = that.options, + container; + + that.element.setAttribute('autocomplete', 'off'); + + // html() deals with many types: htmlString or Element or Array or jQuery + that.noSuggestionsContainer = $('
') + .html(this.options.noSuggestionNotice) + .get(0); + + that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass); + + container = $(that.suggestionsContainer); + + container.appendTo(options.appendTo || 'body'); + + // Only set width if it was provided: + if (options.width !== 'auto') { + container.css('width', options.width); + } + + // Listen for mouse over event on suggestions list: + container.on('mouseover.autocomplete', suggestionSelector, function () { + that.activate($(this).data('index')); + }); + + // Deselect active element when mouse leaves suggestions container: + container.on('mouseout.autocomplete', function () { + that.selectedIndex = -1; + container.children('.' + selected).removeClass(selected); + }); + + // Listen for click event on suggestions list: + container.on('click.autocomplete', suggestionSelector, function () { + that.select($(this).data('index')); + }); + + container.on('click.autocomplete', function () { + clearTimeout(that.blurTimeoutId); + }); + + that.fixPositionCapture = function () { + if (that.visible) { + that.fixPosition(); + } + }; + + $(window).on('resize.autocomplete', that.fixPositionCapture); + + that.el.on('keydown.autocomplete', function (e) { + that.onKeyPress(e); + }); + that.el.on('keyup.autocomplete', function (e) { + that.onKeyUp(e); + }); + that.el.on('blur.autocomplete', function () { + that.onBlur(); + }); + that.el.on('focus.autocomplete', function () { + that.onFocus(); + }); + that.el.on('change.autocomplete', function (e) { + that.onKeyUp(e); + }); + that.el.on('input.autocomplete', function (e) { + that.onKeyUp(e); + }); + }, + + onFocus: function () { + var that = this; + + if (that.disabled) { + return; + } + + that.fixPosition(); + + if (that.el.val().length >= that.options.minChars) { + that.onValueChange(); + } + }, + + onBlur: function () { + var that = this, + options = that.options, + value = that.el.val(), + query = that.getQuery(value); + + // If user clicked on a suggestion, hide() will + // be canceled, otherwise close suggestions + that.blurTimeoutId = setTimeout(function () { + that.hide(); + + if (that.selection && that.currentValue !== query) { + (options.onInvalidateSelection || $.noop).call(that.element); + } + }, 200); + }, + + abortAjax: function () { + var that = this; + if (that.currentRequest) { + that.currentRequest.abort(); + that.currentRequest = null; + } + }, + + setOptions: function (suppliedOptions) { + var that = this, + options = $.extend({}, that.options, suppliedOptions); + + that.isLocal = Array.isArray(options.lookup); + + if (that.isLocal) { + options.lookup = that.verifySuggestionsFormat(options.lookup); + } + + options.orientation = that.validateOrientation(options.orientation, 'bottom'); + + // Adjust height, width and z-index: + $(that.suggestionsContainer).css({ + 'max-height': options.maxHeight + 'px', + width: options.width + 'px', + 'z-index': options.zIndex, + }); + + this.options = options; + }, + + clearCache: function () { + this.cachedResponse = {}; + this.badQueries = []; + }, + + clear: function () { + this.clearCache(); + this.currentValue = ''; + this.suggestions = []; + }, + + disable: function () { + var that = this; + that.disabled = true; + clearTimeout(that.onChangeTimeout); + that.abortAjax(); + }, + + enable: function () { + this.disabled = false; + }, + + fixPosition: function () { + // Use only when container has already its content + + var that = this, + $container = $(that.suggestionsContainer), + containerParent = $container.parent().get(0); + // Fix position automatically when appended to body. + // In other cases force parameter must be given. + if (containerParent !== document.body && !that.options.forceFixPosition) { + return; + } + + // Choose orientation + var orientation = that.options.orientation, + containerHeight = $container.outerHeight(), + height = that.el.outerHeight(), + offset = that.el.offset(), + styles = { top: offset.top, left: offset.left }; + + if (orientation === 'auto') { + var viewPortHeight = $(window).height(), + scrollTop = $(window).scrollTop(), + topOverflow = -scrollTop + offset.top - containerHeight, + bottomOverflow = + scrollTop + viewPortHeight - (offset.top + height + containerHeight); + + orientation = + Math.max(topOverflow, bottomOverflow) === topOverflow ? 'top' : 'bottom'; + } + + if (orientation === 'top') { + styles.top += -containerHeight; + } else { + styles.top += height; + } + + // If container is not positioned to body, + // correct its position using offset parent offset + if (containerParent !== document.body) { + var opacity = $container.css('opacity'), + parentOffsetDiff; + + if (!that.visible) { + $container.css('opacity', 0).show(); + } + + parentOffsetDiff = $container.offsetParent().offset(); + styles.top -= parentOffsetDiff.top; + styles.top += containerParent.scrollTop; + styles.left -= parentOffsetDiff.left; + + if (!that.visible) { + $container.css('opacity', opacity).hide(); + } + } + + if (that.options.width === 'auto') { + styles.width = that.el.outerWidth() + 'px'; + } + + $container.css(styles); + }, + + isCursorAtEnd: function () { + var that = this, + valLength = that.el.val().length, + selectionStart = that.element.selectionStart, + range; + + if (typeof selectionStart === 'number') { + return selectionStart === valLength; + } + if (document.selection) { + range = document.selection.createRange(); + range.moveStart('character', -valLength); + return valLength === range.text.length; + } + return true; + }, + + onKeyPress: function (e) { + var that = this; + + // If suggestions are hidden and user presses arrow down, display suggestions: + if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) { + that.suggest(); + return; + } + + if (that.disabled || !that.visible) { + return; + } + + switch (e.which) { + case keys.ESC: + that.el.val(that.currentValue); + that.hide(); + break; + case keys.RIGHT: + if (that.hint && that.options.onHint && that.isCursorAtEnd()) { + that.selectHint(); + break; + } + return; + case keys.TAB: + if (that.hint && that.options.onHint) { + that.selectHint(); + return; + } + if (that.selectedIndex === -1) { + that.hide(); + return; + } + that.select(that.selectedIndex); + if (that.options.tabDisabled === false) { + return; + } + break; + case keys.RETURN: + if (that.selectedIndex === -1) { + that.hide(); + return; + } + that.select(that.selectedIndex); + break; + case keys.UP: + that.moveUp(); + break; + case keys.DOWN: + that.moveDown(); + break; + default: + return; + } + + // Cancel event if function did not return: + e.stopImmediatePropagation(); + e.preventDefault(); + }, + + onKeyUp: function (e) { + var that = this; + + if (that.disabled) { + return; + } + + switch (e.which) { + case keys.UP: + case keys.DOWN: + return; + } + + clearTimeout(that.onChangeTimeout); + + if (that.currentValue !== that.el.val()) { + that.findBestHint(); + if (that.options.deferRequestBy > 0) { + // Defer lookup in case when value changes very quickly: + that.onChangeTimeout = setTimeout(function () { + that.onValueChange(); + }, that.options.deferRequestBy); + } else { + that.onValueChange(); + } + } + }, + + onValueChange: function () { + if (this.ignoreValueChange) { + this.ignoreValueChange = false; + return; + } + + var that = this, + options = that.options, + value = that.el.val(), + query = that.getQuery(value); + + if (that.selection && that.currentValue !== query) { + that.selection = null; + (options.onInvalidateSelection || $.noop).call(that.element); + } + + clearTimeout(that.onChangeTimeout); + that.currentValue = value; + that.selectedIndex = -1; + + // Check existing suggestion for the match before proceeding: + if (options.triggerSelectOnValidInput && that.isExactMatch(query)) { + that.select(0); + return; + } + + if (query.length < options.minChars) { + that.hide(); + } else { + that.getSuggestions(query); + } + }, + + isExactMatch: function (query) { + var suggestions = this.suggestions; + + return ( + suggestions.length === 1 && + suggestions[0].value.toLowerCase() === query.toLowerCase() + ); + }, + + getQuery: function (value) { + var delimiter = this.options.delimiter, + parts; + + if (!delimiter) { + return value; + } + parts = value.split(delimiter); + return $.trim(parts[parts.length - 1]); + }, + + getSuggestionsLocal: function (query) { + var that = this, + options = that.options, + queryLowerCase = query.toLowerCase(), + filter = options.lookupFilter, + limit = parseInt(options.lookupLimit, 10), + data; + + data = { + suggestions: $.grep(options.lookup, function (suggestion) { + return filter(suggestion, query, queryLowerCase); + }), + }; + + if (limit && data.suggestions.length > limit) { + data.suggestions = data.suggestions.slice(0, limit); + } + + return data; + }, + + getSuggestions: function (q) { + var response, + that = this, + options = that.options, + serviceUrl = options.serviceUrl, + params, + cacheKey, + ajaxSettings; + + options.params[options.paramName] = q; + + if (options.onSearchStart.call(that.element, options.params) === false) { + return; + } + + params = options.ignoreParams ? null : options.params; + + if ($.isFunction(options.lookup)) { + options.lookup(q, function (data) { + that.suggestions = data.suggestions; + that.suggest(); + options.onSearchComplete.call(that.element, q, data.suggestions); + }); + return; + } + + if (that.isLocal) { + response = that.getSuggestionsLocal(q); + } else { + if ($.isFunction(serviceUrl)) { + serviceUrl = serviceUrl.call(that.element, q); + } + cacheKey = serviceUrl + '?' + $.param(params || {}); + response = that.cachedResponse[cacheKey]; + } + + if (response && Array.isArray(response.suggestions)) { + that.suggestions = response.suggestions; + that.suggest(); + options.onSearchComplete.call(that.element, q, response.suggestions); + } else if (!that.isBadQuery(q)) { + that.abortAjax(); + + ajaxSettings = { + url: serviceUrl, + data: params, + type: options.type, + dataType: options.dataType, + }; + + $.extend(ajaxSettings, options.ajaxSettings); + + that.currentRequest = $.ajax(ajaxSettings) + .done(function (data) { + var result; + that.currentRequest = null; + result = options.transformResult(data, q); + that.processResponse(result, q, cacheKey); + options.onSearchComplete.call(that.element, q, result.suggestions); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown); + }); + } else { + options.onSearchComplete.call(that.element, q, []); + } + }, + + isBadQuery: function (q) { + if (!this.options.preventBadQueries) { + return false; + } + + var badQueries = this.badQueries, + i = badQueries.length; + + while (i--) { + if (q.indexOf(badQueries[i]) === 0) { + return true; + } + } + + return false; + }, + + hide: function () { + var that = this, + container = $(that.suggestionsContainer); + + if ($.isFunction(that.options.onHide) && that.visible) { + that.options.onHide.call(that.element, container); + } + + that.visible = false; + that.selectedIndex = -1; + clearTimeout(that.onChangeTimeout); + $(that.suggestionsContainer).hide(); + that.onHint(null); + }, + + suggest: function () { + if (!this.suggestions.length) { + if (this.options.showNoSuggestionNotice) { + this.noSuggestions(); + } else { + this.hide(); + } + return; + } + + var that = this, + options = that.options, + groupBy = options.groupBy, + formatResult = options.formatResult, + value = that.getQuery(that.currentValue), + className = that.classes.suggestion, + classSelected = that.classes.selected, + container = $(that.suggestionsContainer), + noSuggestionsContainer = $(that.noSuggestionsContainer), + beforeRender = options.beforeRender, + html = '', + category, + formatGroup = function (suggestion) { + var currentCategory = suggestion.data[groupBy]; + + if (category === currentCategory) { + return ''; + } + + category = currentCategory; + + return options.formatGroup(suggestion, category); + }; + + if (options.triggerSelectOnValidInput && that.isExactMatch(value)) { + that.select(0); + return; + } + + // Build suggestions inner HTML: + $.each(that.suggestions, function (i, suggestion) { + if (groupBy) { + html += formatGroup(suggestion, value, i); + } + + html += + '
' + + formatResult(suggestion, value, i) + + '
'; + }); + + this.adjustContainerWidth(); + + noSuggestionsContainer.detach(); + container.html(html); + + if ($.isFunction(beforeRender)) { + beforeRender.call(that.element, container, that.suggestions); + } + + that.fixPosition(); + container.show(); + + // Select first value by default: + if (options.autoSelectFirst) { + that.selectedIndex = 0; + container.scrollTop(0); + container + .children('.' + className) + .first() + .addClass(classSelected); + } + + that.visible = true; + that.findBestHint(); + }, + + noSuggestions: function () { + var that = this, + beforeRender = that.options.beforeRender, + container = $(that.suggestionsContainer), + noSuggestionsContainer = $(that.noSuggestionsContainer); + + this.adjustContainerWidth(); + + // Some explicit steps. Be careful here as it easy to get + // noSuggestionsContainer removed from DOM if not detached properly. + noSuggestionsContainer.detach(); + + // clean suggestions if any + container.empty(); + container.append(noSuggestionsContainer); + + if ($.isFunction(beforeRender)) { + beforeRender.call(that.element, container, that.suggestions); + } + + that.fixPosition(); + + container.show(); + that.visible = true; + }, + + adjustContainerWidth: function () { + var that = this, + options = that.options, + width, + container = $(that.suggestionsContainer); + + // If width is auto, adjust width before displaying suggestions, + // because if instance was created before input had width, it will be zero. + // Also it adjusts if input width has changed. + if (options.width === 'auto') { + width = that.el.outerWidth(); + container.css('width', width > 0 ? width : 300); + } else if (options.width === 'flex') { + // Trust the source! Unset the width property so it will be the max length + // the containing elements. + container.css('width', ''); + } + }, + + findBestHint: function () { + var that = this, + value = that.el.val().toLowerCase(), + bestMatch = null; + + if (!value) { + return; + } + + $.each(that.suggestions, function (i, suggestion) { + var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0; + if (foundMatch) { + bestMatch = suggestion; + } + return !foundMatch; + }); + + that.onHint(bestMatch); + }, + + onHint: function (suggestion) { + var that = this, + onHintCallback = that.options.onHint, + hintValue = ''; + + if (suggestion) { + hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length); + } + if (that.hintValue !== hintValue) { + that.hintValue = hintValue; + that.hint = suggestion; + if ($.isFunction(onHintCallback)) { + onHintCallback.call(that.element, hintValue); + } + } + }, + + verifySuggestionsFormat: function (suggestions) { + // If suggestions is string array, convert them to supported format: + if (suggestions.length && typeof suggestions[0] === 'string') { + return $.map(suggestions, function (value) { + return { value: value, data: null }; + }); + } + + return suggestions; + }, + + validateOrientation: function (orientation, fallback) { + orientation = $.trim(orientation || '').toLowerCase(); + + if ($.inArray(orientation, ['auto', 'bottom', 'top']) === -1) { + orientation = fallback; + } + + return orientation; + }, + + processResponse: function (result, originalQuery, cacheKey) { + var that = this, + options = that.options; + + result.suggestions = that.verifySuggestionsFormat(result.suggestions); + + // Cache results if cache is not disabled: + if (!options.noCache) { + that.cachedResponse[cacheKey] = result; + if (options.preventBadQueries && !result.suggestions.length) { + that.badQueries.push(originalQuery); + } + } + + // Return if originalQuery is not matching current query: + if (originalQuery !== that.getQuery(that.currentValue)) { + return; + } + + that.suggestions = result.suggestions; + that.suggest(); + }, + + activate: function (index) { + var that = this, + activeItem, + selected = that.classes.selected, + container = $(that.suggestionsContainer), + children = container.find('.' + that.classes.suggestion); + + container.find('.' + selected).removeClass(selected); + + that.selectedIndex = index; + + if (that.selectedIndex !== -1 && children.length > that.selectedIndex) { + activeItem = children.get(that.selectedIndex); + $(activeItem).addClass(selected); + return activeItem; + } + + return null; + }, + + selectHint: function () { + var that = this, + i = $.inArray(that.hint, that.suggestions); + + that.select(i); + }, + + select: function (i) { + var that = this; + that.hide(); + that.onSelect(i); + }, + + moveUp: function () { + var that = this; + + if (that.selectedIndex === -1) { + return; + } + + if (that.selectedIndex === 0) { + $(that.suggestionsContainer) + .children('.' + that.classes.suggestion) + .first() + .removeClass(that.classes.selected); + that.selectedIndex = -1; + that.ignoreValueChange = false; + that.el.val(that.currentValue); + that.findBestHint(); + return; + } + + that.adjustScroll(that.selectedIndex - 1); + }, + + moveDown: function () { + var that = this; + + if (that.selectedIndex === that.suggestions.length - 1) { + return; + } + + that.adjustScroll(that.selectedIndex + 1); + }, + + adjustScroll: function (index) { + var that = this, + activeItem = that.activate(index); + + if (!activeItem) { + return; + } + + var offsetTop, + upperBound, + lowerBound, + heightDelta = $(activeItem).outerHeight(); + + offsetTop = activeItem.offsetTop; + upperBound = $(that.suggestionsContainer).scrollTop(); + lowerBound = upperBound + that.options.maxHeight - heightDelta; + + if (offsetTop < upperBound) { + $(that.suggestionsContainer).scrollTop(offsetTop); + } else if (offsetTop > lowerBound) { + $(that.suggestionsContainer).scrollTop( + offsetTop - that.options.maxHeight + heightDelta + ); + } + + if (!that.options.preserveInput) { + // During onBlur event, browser will trigger "change" event, + // because value has changed, to avoid side effect ignore, + // that event, so that correct suggestion can be selected + // when clicking on suggestion with a mouse + that.ignoreValueChange = true; + that.el.val(that.getValue(that.suggestions[index].value)); + } + + that.onHint(null); + }, + + onSelect: function (index) { + var that = this, + onSelectCallback = that.options.onSelect, + suggestion = that.suggestions[index]; + + that.currentValue = that.getValue(suggestion.value); + + if (that.currentValue !== that.el.val() && !that.options.preserveInput) { + that.el.val(that.currentValue); + } + + that.onHint(null); + that.suggestions = []; + that.selection = suggestion; + + if ($.isFunction(onSelectCallback)) { + onSelectCallback.call(that.element, suggestion); + } + }, + + getValue: function (value) { + var that = this, + delimiter = that.options.delimiter, + currentValue, + parts; + + if (!delimiter) { + return value; + } + + currentValue = that.currentValue; + parts = currentValue.split(delimiter); + + if (parts.length === 1) { + return value; + } + + return ( + currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value + ); + }, + + dispose: function () { + var that = this; + that.el.off('.autocomplete').removeData('autocomplete'); + $(window).off('resize.autocomplete', that.fixPositionCapture); + $(that.suggestionsContainer).remove(); + }, + }; + + // Create chainable jQuery plugin: + $.fn.devbridgeAutocomplete = function (options, args) { + var dataKey = 'autocomplete'; + // If function invoked without argument return + // instance of the first matched element: + if (!arguments.length) { + return this.first().data(dataKey); + } + + return this.each(function () { + var inputElement = $(this), + instance = inputElement.data(dataKey); + + if (typeof options === 'string') { + if (instance && typeof instance[options] === 'function') { + instance[options](args); + } + } else { + // If instance already exists, destroy it: + if (instance && instance.dispose) { + instance.dispose(); + } + instance = new Autocomplete(this, options); + inputElement.data(dataKey, instance); + } + }); + }; + + // Don't overwrite if it already exists + if (!$.fn.autocomplete) { + $.fn.autocomplete = $.fn.devbridgeAutocomplete; + } +}); From 9674342589da15148e12212152a384411ecf7afd Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 16:19:23 -0500 Subject: [PATCH 04/17] Removed reference to deleted code --- app/views/projects/_approve_form.html.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/_approve_form.html.erb b/app/views/projects/_approve_form.html.erb index 526d0010..9a7a5299 100644 --- a/app/views/projects/_approve_form.html.erb +++ b/app/views/projects/_approve_form.html.erb @@ -1,11 +1,10 @@ <%= render 'form_errors' %> -<%= render 'data_list' %>
/ - > + >
From bc7bdc543835d20e6353fe3973168c8ea4c0ba7a Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 16:20:22 -0500 Subject: [PATCH 05/17] Ignore rspec report --- .gitignore | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 82a5b926..930d25dc 100644 --- a/.gitignore +++ b/.gitignore @@ -65,11 +65,14 @@ node_modules # Do not check aterm.jar into the repo aterm.jar -public +public doc # Ignore test created to not get accidentally added to commit project_create_8.txt # Ignore .yardoc -.yardoc/ \ No newline at end of file +.yardoc/ + +# Ignore rspec report +/spec/fixtures/failed_tests.txt From bbddbdeea32ae64bd08cf4ea5c2157b5ad453c0d Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 16:35:10 -0500 Subject: [PATCH 06/17] Fixed test logic --- spec/models/mediaflux/time_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/models/mediaflux/time_spec.rb b/spec/models/mediaflux/time_spec.rb index 30789a93..4d06b8c8 100644 --- a/spec/models/mediaflux/time_spec.rb +++ b/spec/models/mediaflux/time_spec.rb @@ -23,7 +23,11 @@ initial_tz = xml_snip.xpath("./@tz").text final_tz = instance.convert(xml_snip:) +<<<<<<< HEAD expect(["-04:00", "-05:00"].any? { |tz| final_tz.include?(tz) }).to be_truthy #America/New_York changes based on daylights savings time +======= + expect(final_tz.include?("-04:00") || final_tz.include?("-05:00")).to be true #America/New_York changes based on daylights savings time +>>>>>>> 267e2dc (Fixed test logic) end end describe "date formatting" do From c2b59ed41af19b72b095c49bb81cc2042c78f8fb Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 7 Nov 2024 17:30:12 -0500 Subject: [PATCH 07/17] Tweaking the autocomplete validation --- app/assets/stylesheets/application.scss | 4 ++++ app/views/projects/_edit_form.html.erb | 26 ++++++++++++++++++++++++- tmp/.keep | 0 tmp/pids/.keep | 0 tmp/storage/.keep | 0 5 files changed, 29 insertions(+), 1 deletion(-) delete mode 100644 tmp/.keep delete mode 100644 tmp/pids/.keep delete mode 100644 tmp/storage/.keep diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index c8593431..3059b95f 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -75,3 +75,7 @@ body { content: "▾"; margin-left: -20px; } + +.autocomplete-error { + color: red; +} diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index f362c5bc..f378d4ea 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -16,6 +16,7 @@
This field is required.
+ <% else %> @@ -39,6 +40,7 @@
+ <% else %> <%= @project.metadata_model.data_manager %> @@ -61,6 +63,7 @@
+
@@ -82,6 +85,7 @@
+
@@ -229,13 +233,33 @@ // Autocomplete documentation: https://github.com/devbridge/jQuery-Autocomplete var setupAutocomplete = function(elementId, data) { $(elementId).autocomplete({ - minChars: 0, + minChars: 1, autoSelectFirst: true, + showNoSuggestionNotice: true, + noSuggestionNotice: "No results found", lookup: data, onSelect: function (suggestion) { $(elementId).val(suggestion.data); } }); + + $(elementId).on("focusout", function(event) { + const errorId = elementId + "_error"; + const value = event.target.value.trim(); + if (value === "") { + // TODO: only some fields are required + $(errorId).text("This field is required"); + return; + } + $(errorId).text(""); + for(let i = 0; i < data.length; i++) { + if (value === data[i].data) { + // we got a match + return; + } + } + $(errorId).text("Invalid value entered"); + }); } // Give focus to the autocomplete textbox when the arrow is clicked diff --git a/tmp/.keep b/tmp/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/tmp/pids/.keep b/tmp/pids/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/tmp/storage/.keep b/tmp/storage/.keep deleted file mode 100644 index e69de29b..00000000 From 7f71a39fcf39aa3250538fc07260386fab895a6c Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Fri, 8 Nov 2024 10:03:02 -0500 Subject: [PATCH 08/17] Tweak validation code --- app/assets/stylesheets/application.scss | 4 ++ app/views/projects/_edit_form.html.erb | 59 ++++++++++++++++--------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 3059b95f..c9420c80 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -35,6 +35,10 @@ body { } } +.hidden-element { + display: none; +} + /* These are the styles used by the jQuery autocomplete plug-in */ .autocomplete-suggestions { border: 1px solid #999; diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index f378d4ea..b8a7bb27 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -16,6 +16,7 @@
This field is required.
+ <% else %> + <% else %> @@ -63,7 +65,8 @@
- + +
@@ -85,7 +88,8 @@
- + +
@@ -229,9 +233,35 @@ // because otherwise we don't have access to $(elementId).autocomplete() // This is due to the way we are loading the autocomplete outside of application.js $(function() { + + var validateSelection = function(value, elementId, data, required) { + const errorId = elementId + "_error"; + var matchFound = false; + + $(errorId).text(""); + $(errorId).addClass("hidden-element"); + if (value === "") { + if(required === true) { + $(errorId).removeClass("hidden-element"); + $(errorId).text("This field is required"); + } + } else { + for(let i = 0; i < data.length; i++) { + if (value === data[i].data) { + matchFound = true; + break; + } + } + if (!matchFound) { + $(errorId).removeClass("hidden-element"); + $(errorId).text("Invalid value entered"); + } + } + } + // Setup the autocomplete for a given `elementId` with the `data` provided. // Autocomplete documentation: https://github.com/devbridge/jQuery-Autocomplete - var setupAutocomplete = function(elementId, data) { + var setupAutocomplete = function(elementId, data, required) { $(elementId).autocomplete({ minChars: 1, autoSelectFirst: true, @@ -244,21 +274,8 @@ }); $(elementId).on("focusout", function(event) { - const errorId = elementId + "_error"; const value = event.target.value.trim(); - if (value === "") { - // TODO: only some fields are required - $(errorId).text("This field is required"); - return; - } - $(errorId).text(""); - for(let i = 0; i < data.length; i++) { - if (value === data[i].data) { - // we got a match - return; - } - } - $(errorId).text("Invalid value entered"); + validateSelection(value, elementId, data, required); }); } @@ -268,9 +285,9 @@ return false; }) - setupAutocomplete("#data_sponsor", [ <%= sponsor_list_json %> ]); - setupAutocomplete("#data_manager", [ <%= manager_list_json %> ]); - setupAutocomplete("#ro-user-uid-to-add", [ <%= all_users_list_json %> ]); - setupAutocomplete("#rw-user-uid-to-add", [ <%= all_users_list_json %> ]); + setupAutocomplete("#data_sponsor", [ <%= sponsor_list_json %> ], true); + setupAutocomplete("#data_manager", [ <%= manager_list_json %> ], true); + setupAutocomplete("#ro-user-uid-to-add", [ <%= all_users_list_json %> ], false); + setupAutocomplete("#rw-user-uid-to-add", [ <%= all_users_list_json %> ], false); }); From 4d97c67a93d223938afd86bd33bfa086a8488ffc Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Wed, 13 Nov 2024 16:45:54 -0500 Subject: [PATCH 09/17] Fix errors in project_spec validation --- app/views/projects/_edit_form.html.erb | 35 +++++++++++++++++++++++--- spec/system/project_spec.rb | 16 ++++++++---- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index b8a7bb27..35e4723f 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -236,7 +236,7 @@ var validateSelection = function(value, elementId, data, required) { const errorId = elementId + "_error"; - var matchFound = false; + var validSelection = false; $(errorId).text(""); $(errorId).addClass("hidden-element"); @@ -244,19 +244,22 @@ if(required === true) { $(errorId).removeClass("hidden-element"); $(errorId).text("This field is required"); + } else { + validSelection = true; } } else { for(let i = 0; i < data.length; i++) { if (value === data[i].data) { - matchFound = true; + validSelection = true; break; } } - if (!matchFound) { + if (!validSelection) { $(errorId).removeClass("hidden-element"); $(errorId).text("Invalid value entered"); } } + return validSelection; } // Setup the autocomplete for a given `elementId` with the `data` provided. @@ -275,16 +278,40 @@ $(elementId).on("focusout", function(event) { const value = event.target.value.trim(); - validateSelection(value, elementId, data, required); + if (validateSelection(value, elementId, data, required) === false) { + // If this field is not valid, the form is not valid + invalidateForm(); + } else { + // Checks all fields in the form just to be sure + validateForm(); + } }); } // Give focus to the autocomplete textbox when the arrow is clicked + // This is needed because the arrow itself is not part of the textbox, + // it just _looks_ like it is, but it is a separate HTML element. $(".autocomplete-arrow").on("click", function (event) { $(event.currentTarget).prev().focus(); return false; }) + var invalidateForm = function() { + console.log("Marking form as not valid"); + $("#submit-btn").prop("disabled", true); + } + + var validateForm = function() { + console.log("Checking if the form is valid"); + var v1 = validateSelection($("#data_sponsor").val(), "#data_sponsor", [ <%= sponsor_list_json %> ], true); + var v2 = validateSelection($("#data_manager").val(), "#data_manager", [ <%= manager_list_json %> ], true); + var v3 = validateSelection($("#ro-user-uid-to-add").val(), "#ro-user-uid-to-add", [ <%= all_users_list_json %> ], false); + var v4 = validateSelection($("#rw-user-uid-to-add").val(), "#rw-user-uid-to-add", [ <%= all_users_list_json %> ], false); + var valid = (v1 && v2 && v3 && v4); + console.log(`Valid to submit: ${valid} (${v1}, ${v2}, ${v3}, ${v4})` ); + $("#submit-btn").prop("disabled", !valid); + } + setupAutocomplete("#data_sponsor", [ <%= sponsor_list_json %> ], true); setupAutocomplete("#data_manager", [ <%= manager_list_json %> ], true); setupAutocomplete("#ro-user-uid-to-add", [ <%= all_users_list_json %> ], false); diff --git a/spec/system/project_spec.rb b/spec/system/project_spec.rb index 3bae707c..0d1f0d44 100644 --- a/spec/system/project_spec.rb +++ b/spec/system/project_spec.rb @@ -251,12 +251,18 @@ expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid fill_in "data_manager", with: "xxx" +<<<<<<< HEAD # expect page to have the custom validation error message # Without removing the focus from the form field, the "change" event is not propagated for the DOM page.find("body").click expect(page).to have_content("This field is required.") fill_in "ro-user-uid-to-add", with: read_only.uid +======= + page.find("body").click + sleep(0.5) # give time to the validation to kick-in + expect(page.find("#data_manager_error").text).to eq "Invalid value entered" +>>>>>>> cf5a639 (Fix errors in project_spec validation) # Without removing the focus from the form field, the "change" event is not propagated for the DOM page.find("body").click click_on "btn-add-ro-user" @@ -280,17 +286,17 @@ fill_in "data_manager", with: data_manager.uid fill_in "ro-user-uid-to-add", with: "xxx" page.find("body").click - expect(page.find("#ro-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." + sleep(0.5) # give time to the validation to kick-in + expect(page.find("#ro-user-uid-to-add_error").text).to eq "Invalid value entered" fill_in "rw-user-uid-to-add", with: "zzz" page.find("body").click - expect(page.find("#ro-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." + sleep(0.5) # give time to the validation to kick-in + expect(page.find("#rw-user-uid-to-add_error").text).to eq "Invalid value entered" fill_in "project_directory", with: "test_project" fill_in "title", with: "My test project" expect(page).to have_content("/td-test-001/") - expect do - click_on "Submit" - end.not_to have_enqueued_job(ActionMailer::MailDeliveryJob).exactly(1).times + expect(page.find("button[value=Submit]")).to be_disabled end end context "upon cancelation" do From 11fc5a0e70fd749ae86abeb4c5f8720eb08f958f Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Wed, 13 Nov 2024 17:16:09 -0500 Subject: [PATCH 10/17] Found the magic incantation to trigger focusout and the validation. Still need to fix a few more broken test --- spec/system/project_roles_spec.rb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/spec/system/project_roles_spec.rb b/spec/system/project_roles_spec.rb index 88136cff..5929ea9e 100644 --- a/spec/system/project_roles_spec.rb +++ b/spec/system/project_roles_spec.rb @@ -22,19 +22,39 @@ it "allows the user fill in only valid users for roles" do sign_in sponsor_user visit "/" +<<<<<<< HEAD click_on "Create new project" +======= + click_on "New Project" + byebug +>>>>>>> fee52f0 (Found the magic incantation to trigger focusout and the validation. Still need to fix a few more broken test) # Data Sponsor is not editable. It can only be the user who is initiating this request. expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid fill_in "data_manager", with: "xxx" + element = find("#data_manager") + # https://www.grepper.com/answers/723997/focusout+event+in+capybara + element.native.send_keys :tab + # element.trigger("focusout") + # page.focusout("#data_manager") page.find("body").click + sleep(1.5) expect(page.find("button[value=Submit]")).to be_disabled +<<<<<<< HEAD fill_in "data_manager", with: "" expect(page.find("button[value=Submit]")).to be_disabled +======= + expect(page.find("#data_manager_error").text).to eq "Invalid value entered" + fill_in "data_manager", with: "" + expect(page.find("button[value=Submit]")).to be_disabled + expect(page.find("#data_manager_error").text).to eq "This field is required" +>>>>>>> fee52f0 (Found the magic incantation to trigger focusout and the validation. Still need to fix a few more broken test) fill_in "data_manager", with: data_manager.uid + element.native.send_keys :tab page.find("body").click + sleep(1.5) + expect(page.find("#data_manager_error", visible: false).text).to eq "" click_on "Submit" - expect(page.find("#data_manager").native.attribute("validationMessage")).to eq "" # clicking on Save because once the button is disabled it does not get reenabled until after the user clicks out of the text box fill_in "ro-user-uid-to-add", with: "xxx" From 8e53c6be180db838023681c16527de22559d4e9f Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 11:14:01 -0500 Subject: [PATCH 11/17] Fixed tests --- app/views/projects/_edit_form.html.erb | 33 ++++++++++------- spec/rails_helper.rb | 8 +++++ spec/system/project_roles_spec.rb | 50 +++++++++++--------------- spec/system/project_spec.rb | 20 ++++------- 4 files changed, 55 insertions(+), 56 deletions(-) diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index 35e4723f..da2c3280 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -231,10 +231,10 @@ diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index fc55566a..b57cce27 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -88,3 +88,11 @@ end end end + + +# Mimics Capybara `fill_in` but issues a "tab" keystroke at the end +# so that validations on the textbox (if any) kick-in. +def fill_in_and_out(elementId, with:) + fill_in elementId, with: with + find("#" + elementId).native.send_keys :tab # Tab out of the textbox (https://www.grepper.com/answers/723997/focusout+event+in+capybara) +end diff --git a/spec/system/project_roles_spec.rb b/spec/system/project_roles_spec.rb index 5929ea9e..9349f06b 100644 --- a/spec/system/project_roles_spec.rb +++ b/spec/system/project_roles_spec.rb @@ -22,14 +22,10 @@ it "allows the user fill in only valid users for roles" do sign_in sponsor_user visit "/" -<<<<<<< HEAD click_on "Create new project" -======= click_on "New Project" - byebug ->>>>>>> fee52f0 (Found the magic incantation to trigger focusout and the validation. Still need to fix a few more broken test) - # Data Sponsor is not editable. It can only be the user who is initiating this request. + # Check data manager validations (invalid value, empty value, valid value) expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid fill_in "data_manager", with: "xxx" element = find("#data_manager") @@ -40,49 +36,43 @@ page.find("body").click sleep(1.5) expect(page.find("button[value=Submit]")).to be_disabled -<<<<<<< HEAD fill_in "data_manager", with: "" expect(page.find("button[value=Submit]")).to be_disabled -======= + fill_in_and_out "data_manager", with: "xxx" expect(page.find("#data_manager_error").text).to eq "Invalid value entered" - fill_in "data_manager", with: "" expect(page.find("button[value=Submit]")).to be_disabled + fill_in "data_manager", with: "" expect(page.find("#data_manager_error").text).to eq "This field is required" ->>>>>>> fee52f0 (Found the magic incantation to trigger focusout and the validation. Still need to fix a few more broken test) fill_in "data_manager", with: data_manager.uid element.native.send_keys :tab page.find("body").click sleep(1.5) + expect(page.find("button[value=Submit]")).to be_disabled + fill_in_and_out "data_manager", with: data_manager.uid expect(page.find("#data_manager_error", visible: false).text).to eq "" - click_on "Submit" + expect(page.find("button[value=Submit]").disabled?).to be false - # clicking on Save because once the button is disabled it does not get reenabled until after the user clicks out of the text box - fill_in "ro-user-uid-to-add", with: "xxx" - page.find("body").click + # Check read-only user validations (invalid value, empty value, valid value) + fill_in_and_out "ro-user-uid-to-add", with: "xxx" + expect(page.find("#ro-user-uid-to-add_error").text).to eq "Invalid value entered" expect(page.find("#btn-add-ro-user")).to be_disabled - expect(page.find("#ro-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." - fill_in "ro-user-uid-to-add", with: "" - page.find("body").click + fill_in_and_out "ro-user-uid-to-add", with: "" expect(page.find("#btn-add-ro-user")).to be_disabled - expect(page.find("#ro-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." - fill_in "ro-user-uid-to-add", with: read_only.uid - page.find("body").click + expect(page.find("#ro-user-uid-to-add_error", visible: false).text).to eq "" + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid click_on "btn-add-ro-user" - expect(page.find("#ro-user-uid-to-add").native.attribute("validationMessage")).to eq "" + expect(page.find("#ro-user-uid-to-add", visible: false).text).to eq "" - # clicking on Save because once the button is disabled it does not get reenabled until after the user clicks out of the text box - fill_in "rw-user-uid-to-add", with: "xxx" - page.find("body").click + # Check read-write user validations (invalid value, empty value, valid value) + fill_in_and_out "rw-user-uid-to-add", with: "xxx" expect(page.find("#btn-add-rw-user")).to be_disabled - expect(page.find("#rw-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." - fill_in "rw-user-uid-to-add", with: "" - page.find("body").click + expect(page.find("#rw-user-uid-to-add_error").text).to eq "Invalid value entered" + fill_in_and_out "rw-user-uid-to-add", with: "" expect(page.find("#btn-add-rw-user")).to be_disabled - expect(page.find("#rw-user-uid-to-add").native.attribute("validationMessage")).to eq "Please select a valid value." - fill_in "rw-user-uid-to-add", with: read_write.uid - click_on "Submit" - click_on "btn-add-rw-user" + expect(page.find("#rw-user-uid-to-add_error", visible: false).text).to eq "" + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid expect(page.find("#rw-user-uid-to-add").native.attribute("validationMessage")).to eq "" + click_on "btn-add-rw-user" page.find("#departments").find(:xpath, "option[3]").select_option diff --git a/spec/system/project_spec.rb b/spec/system/project_spec.rb index 0d1f0d44..1cf2f6aa 100644 --- a/spec/system/project_spec.rb +++ b/spec/system/project_spec.rb @@ -249,26 +249,22 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: "xxx" -<<<<<<< HEAD # expect page to have the custom validation error message # Without removing the focus from the form field, the "change" event is not propagated for the DOM page.find("body").click expect(page).to have_content("This field is required.") fill_in "ro-user-uid-to-add", with: read_only.uid -======= page.find("body").click sleep(0.5) # give time to the validation to kick-in expect(page.find("#data_manager_error").text).to eq "Invalid value entered" ->>>>>>> cf5a639 (Fix errors in project_spec validation) # Without removing the focus from the form field, the "change" event is not propagated for the DOM page.find("body").click + fill_in_and_out "data_manager", with: "xxx" + expect(page.find("#data_manager_error").text).to eq "Invalid value entered" click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid click_on "btn-add-rw-user" fill_in "project_directory", with: "test_project" fill_in "title", with: "My test project" @@ -283,15 +279,11 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: "xxx" - page.find("body").click - sleep(0.5) # give time to the validation to kick-in + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: "xxx" expect(page.find("#ro-user-uid-to-add_error").text).to eq "Invalid value entered" - fill_in "rw-user-uid-to-add", with: "zzz" - page.find("body").click - sleep(0.5) # give time to the validation to kick-in + fill_in_and_out "rw-user-uid-to-add", with: "zzz" expect(page.find("#rw-user-uid-to-add_error").text).to eq "Invalid value entered" fill_in "project_directory", with: "test_project" fill_in "title", with: "My test project" From 41b2d935210bd3967b170d5b9df0783c6571e0ba Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 11:19:02 -0500 Subject: [PATCH 12/17] Rubocop nitpicking --- spec/rails_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index b57cce27..f28f94c1 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -89,10 +89,10 @@ end end - # Mimics Capybara `fill_in` but issues a "tab" keystroke at the end # so that validations on the textbox (if any) kick-in. -def fill_in_and_out(elementId, with:) - fill_in elementId, with: with - find("#" + elementId).native.send_keys :tab # Tab out of the textbox (https://www.grepper.com/answers/723997/focusout+event+in+capybara) +def fill_in_and_out(element_id, with:) + fill_in element_id, with: with + # Tab out of the textbox (https://www.grepper.com/answers/723997/focusout+event+in+capybara) + find("#" + element_id).native.send_keys :tab end From cb71b0c874043c47d1886568aaed2fb875442700 Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 17:45:31 -0500 Subject: [PATCH 13/17] Adjusted the validation (since it got messed up during the branch merge) --- app/assets/stylesheets/_settings.scss | 27 +-- app/assets/stylesheets/application.scss | 2 +- app/views/projects/_edit_form.html.erb | 245 +++++++++++------------- 3 files changed, 116 insertions(+), 158 deletions(-) diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss index 04e3c312..e42241d4 100644 --- a/app/assets/stylesheets/_settings.scss +++ b/app/assets/stylesheets/_settings.scss @@ -238,37 +238,14 @@ margin-bottom: 10px; } - .custom_error { - display: none; - - .error_message { - color: var(--Secondary-Red-Dark, #b00002); - margin-top: 2px; - - /* Body XS */ - font-family: "Libre Franklin"; - font-size: 14px; - font-style: normal; - font-weight: 400; - line-height: 21px; /* 150% */ - } - } - - input:valid { + input[type=text]:valid { background-color: rgb(255, 255, 255); } - input:invalid { + input[type=text]:invalid { outline-color: red; } - // write css so that the text displayed on invalid input is red and appears to the right of the div instead of below it - span { - color: red; - display: inline; - margin-left: 10px; - } - h2 { // Headings for roles and description text-decoration: underline; diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index c9420c80..cd43d8fd 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -81,5 +81,5 @@ body { } .autocomplete-error { - color: red; + color: var(--Secondary-Red-Dark, #b00002); } diff --git a/app/views/projects/_edit_form.html.erb b/app/views/projects/_edit_form.html.erb index da2c3280..0e8bb93e 100644 --- a/app/views/projects/_edit_form.html.erb +++ b/app/views/projects/_edit_form.html.erb @@ -9,22 +9,16 @@
<% if current_user.superuser? %> - - - - - + + + + <% else %> - + <%= @project.metadata_model.data_sponsor || current_user.uid %> <% end %>
- +
@@ -33,22 +27,16 @@
<% if !@project.persisted? || @project.metadata_model.data_sponsor == current_user.uid %> - -
- <%= image_tag("custom_error.svg", alt: "This field is required") %> -
This field is required.
-
-
- - - + + + + <% else %> - - <%= @project.metadata_model.data_manager %> + + <%= @project.metadata_model.data_manager %> <% end %>
- +
@@ -72,7 +60,7 @@
- +
@@ -95,7 +83,7 @@
- +

Project Description

<% if current_user.superuser? %> @@ -106,7 +94,7 @@
- + <% end %>
@@ -114,136 +102,114 @@
Required
-
-
- <%= image_tag("custom_error.svg", alt: "This field is required") %> -
This field is required.
-
-
-
+ - <% if @project.in_mediaflux? %> + <% if @project.in_mediaflux? %> -
+ Make the field readonly so the user cannot change it, but leave it as an HTML INPUT so that it is + send back to the controller (we don't want to lose this value) + --> +
- - - +
- +
-
+
<% if (current_user.superuser? || current_user.eligible_sysadmin?) %> -
-
- -
- - - -
-
- -

This project has already been saved to Mediaflux and the project_directory cannot be changed

-
- +
+
+ +
+
+ +

This project has already been saved to Mediaflux and the project_directory cannot be changed

+
+
<% end %> + <% end %> - <% end %> +
+
+ +
Required
+
+
+ <%= @project.project_directory_parent_path %>/ + +
+
-
+
- -
Required
-
Required
+ +
Required
-
- <%= @project.project_directory_parent_path %>/ - -
- <%= image_tag("custom_error.svg", alt: "This field is required") %> -
This field is required.
+
-
- - -
-
- - -
Required
-
-
- - -
-
-
+
-
-
- - - -
-
- - - -
-
- - - - - -
- - <% if params[:action] == 'edit' %> - <%= link_to "Cancel", @project, class: "btn btn-secondary", id: "cancel-btn" %> - <% else %> - <%= link_to "Cancel", root_path, class: "btn btn-secondary", id: "cancel-btn" %> - <% end %> -
+
+
+ +
+
+ +
+
+ + + +
+ + <% if params[:action] == 'edit' %> + <%= link_to "Cancel", @project, class: "btn btn-secondary", id: "cancel-btn" %> + <% else %> + <%= link_to "Cancel", root_path, class: "btn btn-secondary", id: "cancel-btn" %> + <% end %> +
-
+
From a3bce6fb63b8a90cf7284e5235960caab229ce2b Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 18:07:13 -0500 Subject: [PATCH 14/17] Fixed another test that I messed up in the branch merge --- spec/system/project_roles_spec.rb | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/spec/system/project_roles_spec.rb b/spec/system/project_roles_spec.rb index 9349f06b..0d714e4d 100644 --- a/spec/system/project_roles_spec.rb +++ b/spec/system/project_roles_spec.rb @@ -23,31 +23,16 @@ sign_in sponsor_user visit "/" click_on "Create new project" - click_on "New Project" # Check data manager validations (invalid value, empty value, valid value) expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: "xxx" - element = find("#data_manager") - # https://www.grepper.com/answers/723997/focusout+event+in+capybara - element.native.send_keys :tab - # element.trigger("focusout") - # page.focusout("#data_manager") - page.find("body").click - sleep(1.5) - expect(page.find("button[value=Submit]")).to be_disabled - fill_in "data_manager", with: "" - expect(page.find("button[value=Submit]")).to be_disabled fill_in_and_out "data_manager", with: "xxx" expect(page.find("#data_manager_error").text).to eq "Invalid value entered" expect(page.find("button[value=Submit]")).to be_disabled - fill_in "data_manager", with: "" - expect(page.find("#data_manager_error").text).to eq "This field is required" - fill_in "data_manager", with: data_manager.uid - element.native.send_keys :tab - page.find("body").click - sleep(1.5) + fill_in_and_out "data_manager", with: "" expect(page.find("button[value=Submit]")).to be_disabled + fill_in_and_out "data_manager", with: "" + expect(page.find("#data_manager_error").text).to eq "This field is required" fill_in_and_out "data_manager", with: data_manager.uid expect(page.find("#data_manager_error", visible: false).text).to eq "" expect(page.find("button[value=Submit]").disabled?).to be false @@ -60,7 +45,6 @@ expect(page.find("#btn-add-ro-user")).to be_disabled expect(page.find("#ro-user-uid-to-add_error", visible: false).text).to eq "" fill_in_and_out "ro-user-uid-to-add", with: read_only.uid - click_on "btn-add-ro-user" expect(page.find("#ro-user-uid-to-add", visible: false).text).to eq "" # Check read-write user validations (invalid value, empty value, valid value) @@ -72,7 +56,6 @@ expect(page.find("#rw-user-uid-to-add_error", visible: false).text).to eq "" fill_in_and_out "rw-user-uid-to-add", with: read_write.uid expect(page.find("#rw-user-uid-to-add").native.attribute("validationMessage")).to eq "" - click_on "btn-add-rw-user" page.find("#departments").find(:xpath, "option[3]").select_option From c294b3047802708b049d30d6a58eb9b71a17674a Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 18:18:10 -0500 Subject: [PATCH 15/17] Fixed another git merge issue --- spec/models/mediaflux/time_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/models/mediaflux/time_spec.rb b/spec/models/mediaflux/time_spec.rb index 4d06b8c8..30789a93 100644 --- a/spec/models/mediaflux/time_spec.rb +++ b/spec/models/mediaflux/time_spec.rb @@ -23,11 +23,7 @@ initial_tz = xml_snip.xpath("./@tz").text final_tz = instance.convert(xml_snip:) -<<<<<<< HEAD expect(["-04:00", "-05:00"].any? { |tz| final_tz.include?(tz) }).to be_truthy #America/New_York changes based on daylights savings time -======= - expect(final_tz.include?("-04:00") || final_tz.include?("-05:00")).to be true #America/New_York changes based on daylights savings time ->>>>>>> 267e2dc (Fixed test logic) end end describe "date formatting" do From 7c4427d17cf782fe5d41ecc7a70c66e1e1bea687 Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Thu, 14 Nov 2024 18:22:03 -0500 Subject: [PATCH 16/17] Fixed broken tests --- spec/system/project_spec.rb | 78 +++++++++++-------------------------- 1 file changed, 22 insertions(+), 56 deletions(-) diff --git a/spec/system/project_spec.rb b/spec/system/project_spec.rb index 1cf2f6aa..d5b801d2 100644 --- a/spec/system/project_spec.rb +++ b/spec/system/project_spec.rb @@ -177,15 +177,9 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: read_only.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-rw-user" + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid # select a department select "RDSS", from: "departments" fill_in "project_directory", with: "test_project" @@ -225,15 +219,15 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: read_only.uid + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid + # page.find("body").click + # click_on "btn-add-ro-user" + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-rw-user" + # page.find("body").click + # click_on "btn-add-rw-user" fill_in "project_directory", with: "test_project" fill_in "title", with: "My test project" expect(page).to have_content("/td-test-001/") @@ -249,23 +243,12 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: "xxx" - # expect page to have the custom validation error message - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - expect(page).to have_content("This field is required.") - - fill_in "ro-user-uid-to-add", with: read_only.uid - page.find("body").click - sleep(0.5) # give time to the validation to kick-in - expect(page.find("#data_manager_error").text).to eq "Invalid value entered" - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click fill_in_and_out "data_manager", with: "xxx" expect(page.find("#data_manager_error").text).to eq "Invalid value entered" - click_on "btn-add-ro-user" + fill_in_and_out "data_manager", with: "" + expect(page.find("#data_manager_error").text).to eq "This field is required" + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid fill_in_and_out "rw-user-uid-to-add", with: read_write.uid - click_on "btn-add-rw-user" fill_in "project_directory", with: "test_project" fill_in "title", with: "My test project" expect(page).to have_content("/td-test-001/") @@ -314,13 +297,9 @@ visit "/" click_on "Create new project" # Data Sponsor is automatically populated. - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: read_only.uid - page.find("body").click - click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid - page.find("body").click - click_on "btn-add-rw-user" + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid fill_in "project_directory", with: "test?project" valid = page.find("input#project_directory:invalid") expect(valid).to be_truthy @@ -340,15 +319,9 @@ visit "/" click_on "Create new project" expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: read_only.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-rw-user" + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid select "RDSS", from: "departments" fill_in "project_directory", with: FFaker::Name.name.tr(" ", "_") fill_in "title", with: "My test project" @@ -393,20 +366,13 @@ .skipping(:'color-contrast') expect(page.find("#non-editable-data-sponsor").text).to eq sponsor_user.uid - fill_in "data_manager", with: data_manager.uid - fill_in "ro-user-uid-to-add", with: read_only.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-ro-user" - fill_in "rw-user-uid-to-add", with: read_write.uid - # Without removing the focus from the form field, the "change" event is not propagated for the DOM - page.find("body").click - click_on "btn-add-rw-user" + fill_in_and_out "data_manager", with: data_manager.uid + fill_in_and_out "ro-user-uid-to-add", with: read_only.uid + fill_in_and_out "rw-user-uid-to-add", with: read_write.uid select "RDSS", from: "departments" fill_in "project_directory", with: FFaker::Name.name.tr(" ", "_") fill_in "title", with: "My test project" expect(page).to have_content("/td-test-001/") - sleep 1 expect(page.find_all("input:invalid").count).to eq(0) click_on "Submit" # For some reason the above click on submit sometimes does not submit the form From 54e0f5adc2ed61584a3371e42eef6e89a6f2abac Mon Sep 17 00:00:00 2001 From: Hector Correa Date: Fri, 15 Nov 2024 09:46:25 -0500 Subject: [PATCH 17/17] Simplify a couple of tests --- spec/system/project_roles_spec.rb | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/spec/system/project_roles_spec.rb b/spec/system/project_roles_spec.rb index 0d714e4d..86592a39 100644 --- a/spec/system/project_roles_spec.rb +++ b/spec/system/project_roles_spec.rb @@ -193,14 +193,8 @@ it "allows a Data Sponsor to assign a Data User" do sign_in sponsor_user visit "/projects/#{project.id}/edit" - fill_in "ro-user-uid-to-add", with: ro_data_user.uid - page.find("body").click - find(:css, "#btn-add-ro-user").click - page.find("body").click - fill_in "rw-user-uid-to-add", with: rw_data_user.uid - page.find("body").click - find(:css, "#btn-add-rw-user").click - page.find("body").click + fill_in_and_out "ro-user-uid-to-add", with: ro_data_user.uid + fill_in_and_out "rw-user-uid-to-add", with: rw_data_user.uid click_on "Submit" visit "/projects/#{project.id}" expect(page).to have_content "#{ro_data_user.display_name} (read only)" @@ -209,14 +203,8 @@ it "allows a Data Manager to assign a Data User" do sign_in data_manager visit "/projects/#{project.id}/edit" - fill_in "ro-user-uid-to-add", with: ro_data_user.uid - page.find("body").click - find(:css, "#btn-add-ro-user").click - page.find("body").click - fill_in "rw-user-uid-to-add", with: rw_data_user.uid - page.find("body").click - find(:css, "#btn-add-rw-user").click - page.find("body").click + fill_in_and_out "ro-user-uid-to-add", with: ro_data_user.uid + fill_in_and_out "rw-user-uid-to-add", with: rw_data_user.uid click_on "Submit" visit "/projects/#{project.id}" expect(page).to have_content "#{ro_data_user.display_name} (read only)"