From 7ba72c5d4adc5096aa76fc10ca31d1086c2045c3 Mon Sep 17 00:00:00 2001 From: MariaAga Date: Fri, 19 Jul 2024 10:39:13 +0100 Subject: [PATCH 1/2] Fixes #37664 - switch to using js-cookie --- app/assets/javascripts/application.js | 8 -------- package.json | 1 + webpack/assets/javascripts/bundle.js | 2 ++ webpack/assets/javascripts/hosts/tableCheckboxes.js | 7 ++++--- webpack/assets/javascripts/react_app/common/I18n.js | 6 ++++++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 25babb1b055..02ad119b5e4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -79,14 +79,6 @@ function onContentLoad() { password_caps_lock_hint(); - tfm.i18n.intl.ready.then(function() { - var tz = jstz.determine(); - $.cookie('timezone', tz.name(), { - path: '/', - secure: location.protocol === 'https:', - }); - }); - $('.full-value').SelectOnClick(); activate_select2(':root'); diff --git a/package.json b/package.json index 98a59097288..d08b20471eb 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "graphql-tag": "^2.11.0", "intl": "~1.2.5", "jed": "^1.1.1", + "js-cookie": "^3.0.5", "os-browserify": "^0.3.0", "react-intl": "^2.8.0" }, diff --git a/webpack/assets/javascripts/bundle.js b/webpack/assets/javascripts/bundle.js index 397994b337c..8074352d344 100644 --- a/webpack/assets/javascripts/bundle.js +++ b/webpack/assets/javascripts/bundle.js @@ -1,5 +1,6 @@ import 'core-js/shim'; import 'regenerator-runtime/runtime'; +import Cookies from 'js-cookie'; import compute from './foreman_compute_resource'; import componentRegistry from './react_app/components/componentRegistry'; @@ -56,4 +57,5 @@ window.tfm = Object.assign(window.tfm || {}, { componentRegistry, store, lookupKeys, + Cookies, }); diff --git a/webpack/assets/javascripts/hosts/tableCheckboxes.js b/webpack/assets/javascripts/hosts/tableCheckboxes.js index 5e1a1df6c85..56121c4ff69 100644 --- a/webpack/assets/javascripts/hosts/tableCheckboxes.js +++ b/webpack/assets/javascripts/hosts/tableCheckboxes.js @@ -14,6 +14,7 @@ /* eslint-disable jquery/no-in-array */ import $ from 'jquery'; +import Cookies from 'js-cookie'; import { sprintf, @@ -43,7 +44,7 @@ export function hostChecked({ id, checked }) { multipleAlert.data('multiple', false); } } - $.cookie(cookieName, JSON.stringify(foremanSelectedHosts), { + Cookies.set(cookieName, JSON.stringify(foremanSelectedHosts), { secure: window.location.protocol === 'https:', }); toggleActions(); @@ -62,7 +63,7 @@ function rmHostId(id) { function readFromCookie() { try { - const r = $.cookie(cookieName); + const r = Cookies.get(cookieName); if (r) return $.parseJSON(r); return []; } catch (err) { @@ -116,7 +117,7 @@ $(document).on('ContentLoad', () => { }); function removeForemanHostsCookie() { - $.removeCookie(cookieName); + Cookies.remove(cookieName); } export function resetSelection() { diff --git a/webpack/assets/javascripts/react_app/common/I18n.js b/webpack/assets/javascripts/react_app/common/I18n.js index ad2030d70a6..654caf885cb 100644 --- a/webpack/assets/javascripts/react_app/common/I18n.js +++ b/webpack/assets/javascripts/react_app/common/I18n.js @@ -2,6 +2,8 @@ /* eslint-disable import/no-dynamic-require */ import Jed from 'jed'; import { addLocaleData } from 'react-intl'; +import Cookies from 'js-cookie'; +import jstz from 'jstz'; import forceSingleton from './forceSingleton'; class IntlLoader { @@ -18,6 +20,10 @@ class IntlLoader { await this.fetchIntl(); const localeData = require(/* webpackChunkName: 'react-intl/locale/[request]' */ `react-intl/locale-data/${this.locale}`); addLocaleData(localeData); + Cookies.set('timezone', jstz.determine().name(), { + path: '/', + secure: window.location.protocol === 'https:', + }); return true; } From 85e12713f7b4d9e955b0ee7dea37e1a42909129b Mon Sep 17 00:00:00 2001 From: MariaAga Date: Wed, 24 Apr 2024 14:42:30 +0200 Subject: [PATCH 2/2] Fixes #37382 - Update to jQuery 3 --- app/assets/javascripts/application.js | 102 ++++++++++++++++-- app/assets/javascripts/hidden_values.js | 2 +- app/assets/javascripts/host_edit.js | 10 +- .../javascripts/host_edit_interfaces.js | 10 +- app/assets/javascripts/hosts.js | 4 +- app/assets/javascripts/jquery.multi-select.js | 10 +- app/assets/javascripts/lookup_keys.js | 4 +- app/assets/javascripts/parameter_override.js | 4 +- app/helpers/form_helper.rb | 7 +- bundler.d/assets.rb | 2 +- test/integration/compute_profile_js_test.rb | 6 +- test/integration/host_js_test.rb | 6 +- .../provisioning_template_js_test.rb | 15 ++- test/integration/report_template_js_test.rb | 6 +- test/integration/shared/host_finders.rb | 2 +- test/integration/user_test.rb | 4 +- test/integration_test_helper.rb | 26 +++-- .../assets/javascripts/foreman_auth_source.js | 2 +- .../javascripts/foreman_http_proxies.js | 2 +- webpack/assets/javascripts/foreman_tools.js | 17 +-- webpack/assets/javascripts/foreman_users.js | 5 +- .../javascripts/hosts/tableCheckboxes.js | 2 +- 22 files changed, 174 insertions(+), 74 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 02ad119b5e4..c8567762016 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -38,7 +38,7 @@ function onContentLoad() { if ($('input[focus_on_load=true]').length > 0) { $('input[focus_on_load]') .first() - .focus(); + .trigger("focus"); } // highlight tabs with errors @@ -84,11 +84,66 @@ function onContentLoad() { $('input.remove_form_templates') .closest('form') - .submit(function(event) { + .on('submit', function(event) { $(this) .find('.form_template') .remove(); }); + + const autoUpdateSelect2Titles = function() { + const targetNodes = document.querySelectorAll( + '.select2-selection__rendered' + ); + + const config = { attributes: true, attributeFilter: ['title'] }; + + const callback = function(mutationsList, observer) { + for (let mutation of mutationsList) { + if ( + mutation.type === 'attributes' && + mutation.attributeName === 'title' + ) { + mutation.target.setAttribute( + 'data-original-title', + mutation.target.getAttribute('title') + ); + } + } + }; + + targetNodes.forEach(targetNode => { + const observer = new MutationObserver(callback); + observer.observe(targetNode, config); + }); + }; + + const hideSelect2ClearTooltip = function() { + $(document).on('blur', '.select2-selection__clear', function() { + $('.tooltip').tooltip('hide'); + }); + + const targetNode = document.querySelector('body'); + const config = { attributes: false, childList: true, subtree: true }; + const callback = function(mutationsList) { + for (let mutation of mutationsList) { + if (mutation.type === 'childList') { + const node = Array.from(mutation.removedNodes).find( + node => + node.classList && + node.classList.contains('select2-selection__clear') + ); + if (node) { + $('.tooltip').tooltip('hide'); + } + } + } + }; + const observer = new MutationObserver(callback); + observer.observe(targetNode, config); + }; + + hideSelect2ClearTooltip(); + autoUpdateSelect2Titles(); } function preserve_selected_options(elem) { @@ -98,7 +153,7 @@ function preserve_selected_options(elem) { } function password_caps_lock_hint() { - $('[type=password]').keypress(function(e) { + $('[type=password]').trigger('keypress', function(e) { var $addon = $(this) .parent() .children('.input-addon'), @@ -301,7 +356,7 @@ function ignore_subnet(item) { // shows provisioning templates in a new window $(function() { - $('[data-provisioning-template=true]').click(function() { + $('[data-provisioning-template=true]').on('click', function() { window.open(this.href, [ (width = '300'), (height = '400'), @@ -423,18 +478,34 @@ function disableButtonToggle(item, explicit) { $(formControl).val(''); } - $(item).blur(); + $(item).trigger('blur'); } -function activate_select2(container, allowClear) { - allowClear = typeof allowClear !== 'undefined' ? allowClear : true; +function activate_select2(container, allowClear ) { + const htmlElemnt = document.getElementsByTagName('html')[0]; + const langAttr = htmlElemnt.getAttribute('lang') || 'en'; $(container) .find('select:not(.without_select2)') .not('.form_template select') .not('#interfaceForms select') - .select2({ - allowClear: allowClear, - formatNoMatches: __('No matches found'), + .each(function() { + const placeholder = $(this).data('placeholder'); + let selectAllowClear = allowClear + if (typeof selectAllowClear === 'undefined') { + if ($(this).hasClass('include_blank')) { + selectAllowClear = true; + } else { + selectAllowClear = false; + } + } + $(this).select2({ + debug: true, + language: langAttr, + width: '100%', + allowClear: selectAllowClear, + formatNoMatches: __('No matches found'), + placeholder: selectAllowClear? placeholder || '' : { id: '-1', text: '' }, + }); }); } @@ -455,3 +526,14 @@ function clearError(field) { .children('.error-message'); error_block.remove(); } + +// jQuery deprecated functions +// used by gridster and bootstrap +$.fn.isFunction = function(func) { + return typeof func === 'function'; +}; +$.fn.isArray = Array.isArray; +$.fn.trim = String.prototype.trim; +$.fn.bind = function(event, func) { + return this.on(event, func); +}; diff --git a/app/assets/javascripts/hidden_values.js b/app/assets/javascripts/hidden_values.js index 8a52e79102b..ee386774f78 100644 --- a/app/assets/javascripts/hidden_values.js +++ b/app/assets/javascripts/hidden_values.js @@ -3,7 +3,7 @@ function turn_textarea_switch() { } function hidden_value_control() { - $('.toggle-hidden-value a').click(function(event) { + $('.toggle-hidden-value a').on('click', function(event) { event.preventDefault(); var link = $(event.currentTarget); link diff --git a/app/assets/javascripts/host_edit.js b/app/assets/javascripts/host_edit.js index 742a5ea7ad8..57f663fea4b 100644 --- a/app/assets/javascripts/host_edit.js +++ b/app/assets/javascripts/host_edit.js @@ -1,13 +1,11 @@ //= require parameter_override -$(document).ready(function() { +$(document).on('ContentLoad', function() { var searchParams = new URLSearchParams(window.location.search); if(searchParams.has('hostgroup_id')) { var param = searchParams.get('hostgroup_id'); $('#host_hostgroup_id').val(param).trigger('change'); } -}); -$(document).on('ContentLoad', function() { onHostEditLoad(); }); $(document) @@ -145,11 +143,11 @@ function update_capabilities(capabilities) { var build = capabilities.indexOf('build') > -1; if (build) { $('#manage_network_build').show(); - $('#host_provision_method_build').click(); + $('#host_provision_method_build').trigger('click'); build_provision_method_selected(); } else if (capabilities.length > 0) { $('#manage_network_build').hide(); - $('#host_provision_method_' + capabilities[0]).click(); + $('#host_provision_method_' + capabilities[0]).trigger('click'); if (capabilities[0].toLowerCase() === 'image') { image_provision_method_selected(); } @@ -329,7 +327,7 @@ function update_form(element, options) { if (host_compute_resource_id.exists()) { // to handle case if def process_taxonomy changed compute_resource_id to nil if (!host_compute_resource_id.val()) { - host_compute_resource_id.change(); + host_compute_resource_id.trigger('change'); } else { // in case the compute resource was selected, we still want to check for // free ip if applicable diff --git a/app/assets/javascripts/host_edit_interfaces.js b/app/assets/javascripts/host_edit_interfaces.js index 75cc3f5a4f2..4de94404107 100644 --- a/app/assets/javascripts/host_edit_interfaces.js +++ b/app/assets/javascripts/host_edit_interfaces.js @@ -1,6 +1,6 @@ -$(document).ready(function() { - $('#host_name').select(); - $('#host_name').focus(); +$(document).on('ContentLoad', function() { + $('#host_name').trigger("select"); + $('#host_name').trigger("focus"); }); function remove_interface(interface_id) { @@ -107,12 +107,12 @@ function get_interface_row(interface_id) { interface_row.attr('id', 'interface' + interface_id); interface_row.data('interface-id', interface_id); - interface_row.find('.showModal').click(function() { + interface_row.find('.showModal').on('click', function() { edit_interface(interface_id); return false; }); - interface_row.find('.removeInterface').click(function() { + interface_row.find('.removeInterface').on('click', function() { remove_interface(interface_id); return false; }); diff --git a/app/assets/javascripts/hosts.js b/app/assets/javascripts/hosts.js index b5df44012b6..54125e10a97 100644 --- a/app/assets/javascripts/hosts.js +++ b/app/assets/javascripts/hosts.js @@ -2,7 +2,7 @@ $(document).on('ContentLoad', function() { tfm.tools.setTab(); var dialog = $('#review_before_build'); - $('#build-review').click(function() { + $('#build-review').on('click', function() { dialog.find('.modal-body #build_status').html(''); $('.loading').addClass('visible'); $.ajax({ @@ -26,7 +26,7 @@ $(document).on('ContentLoad', function() { }); dialog.on('click', '#recheck_review', function() { - $('#build-review').click(); + $('#build-review').trigger('click'); }); var action_buttons = $('.btn-toolbar a') diff --git a/app/assets/javascripts/jquery.multi-select.js b/app/assets/javascripts/jquery.multi-select.js index dd5bb993dd3..938ce7077eb 100644 --- a/app/assets/javascripts/jquery.multi-select.js +++ b/app/assets/javascripts/jquery.multi-select.js @@ -27,28 +27,28 @@ function multiSelectToolTips(){ var msid = '#ms-'+item.id; // it an
  • items match multiple tooltips, then only the first tooltip will show if (!(mismatches == null || mismatches == 'undefined')) { - var missing_ids = $.parseJSON(mismatches); + var missing_ids = JSON.parse(mismatches); $.each(missing_ids, function(index,missing_id){ opt_id = sanitize(missing_id+''); $(msid).find('li#'+opt_id+'-selectable').addClass('delete').tooltip({container: 'body', title: __("Select this since it belongs to a host"), placement: "left"}); }) } if (!(useds == null || descendants == 'useds')) { - var used_ids = $.parseJSON(useds); + var used_ids = JSON.parse(useds); $.each(used_ids, function(index,used_id){ opt_id = sanitize(used_id+''); $(msid).find('li#'+opt_id+'-selection').addClass('used_by_hosts').tooltip({container: 'body', title: __("This is used by a host"), placement: "right"}); }) } if (!(inheriteds == null || inheriteds == 'undefined')) { - var inherited_ids = $.parseJSON(inheriteds); + var inherited_ids = JSON.parse(inheriteds); $.each(inherited_ids, function(index,inherited_id){ opt_id = sanitize(inherited_id+''); $(msid).find('li#'+opt_id+'-selection').addClass('inherited').tooltip({container: 'body', title: __("This is inherited from parent"), placement: "right"}); }) } if (!(descendants == null || descendants == 'undefined')) { - var descendant_ids = $.parseJSON(descendants); + var descendant_ids = JSON.parse(descendants); $.each(descendant_ids, function(index,descendant_id){ opt_id = sanitize(descendant_id+''); $(msid).find('li#'+opt_id+'-selection').addClass('descendants').tooltip({container: 'body', title: __("Parent is already selected"), placement: "right"}); @@ -77,7 +77,7 @@ $(document).on('click', '.ms-select-all', function () { $(this).tooltip('hide') .closest('.form-group') .find('.ms-selectable .ms-list :visible') - .click(); + .trigger('click'); return false; }); diff --git a/app/assets/javascripts/lookup_keys.js b/app/assets/javascripts/lookup_keys.js index 618636807dc..720155fb771 100644 --- a/app/assets/javascripts/lookup_keys.js +++ b/app/assets/javascripts/lookup_keys.js @@ -28,7 +28,7 @@ function select_first_tab() { pills .find('a:visible') .first() - .click(); + .trigger('click'); } } @@ -39,7 +39,7 @@ function remove_node(item) { .attr('href') ) .children('.btn-danger') - .click(); + .trigger('click'); } function fix_template_context(content, context) { diff --git a/app/assets/javascripts/parameter_override.js b/app/assets/javascripts/parameter_override.js index 1a66b6a7087..cd58ce7d60c 100644 --- a/app/assets/javascripts/parameter_override.js +++ b/app/assets/javascripts/parameter_override.js @@ -8,7 +8,7 @@ function override_param(item) { var v = param_value.val(); var addParameterButton = $('#parameters').find('.btn-primary'); - addParameterButton.click(); + addParameterButton.trigger('click'); var directionOfAddedItems = addParameterButton.attr('direction'); var new_param = $('#parameters').find('.fields'); if(directionOfAddedItems === 'append'){ @@ -26,6 +26,6 @@ function override_param(item) { hiddenValueCheckBox = new_param.find('.set_hidden_value'); hiddenValueCheckBox.prop('checked', true); hiddenValueCheckBox.val('1'); - alink.click(); + alink.trigger('click'); } } diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index 6f4164bd450..bd2409c17a0 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -119,6 +119,9 @@ def select_f(f, attr, array, id, method, select_options = {}, html_options = {}) # that was defined in the struct. blank_option.instance_eval('undef to_s', __FILE__, __LINE__) if method.to_s == 'to_s' || id.to_s == 'to_s' array.insert(0, blank_option) + html_options['data-placeholder'] = blank_value || html_options['placeholder'] + elsif html_options[:placeholder] + html_options['data-placeholder'] = html_options.delete(:placeholder) end select_options[:disabled] = '' if select_options[:disabled] == include_blank @@ -127,7 +130,9 @@ def select_f(f, attr, array, id, method, select_options = {}, html_options = {}) html_options[:size] = 'col-md-10' if html_options[:multiple] field(f, attr, html_options) do addClass html_options, "form-control" - + if include_blank.is_a?(TrueClass) + addClass html_options, "include_blank" + end collection_select = f.collection_select(attr, array, id, method, select_options, html_options) if disable_button diff --git a/bundler.d/assets.rb b/bundler.d/assets.rb index 606f604cc45..cd60de58e7e 100644 --- a/bundler.d/assets.rb +++ b/bundler.d/assets.rb @@ -1,5 +1,5 @@ group :assets do - gem 'jquery-ui-rails', '~> 6.0' + gem 'jquery-ui-rails', '~> 7.0' gem 'patternfly-sass', '~> 3.59.4' gem 'gettext_i18n_rails_js', '~> 1.4' gem 'po_to_json', '~> 1.1' diff --git a/test/integration/compute_profile_js_test.rb b/test/integration/compute_profile_js_test.rb index 41233fa6d60..4f08f703ced 100644 --- a/test/integration/compute_profile_js_test.rb +++ b/test/integration/compute_profile_js_test.rb @@ -38,10 +38,10 @@ class ComputeProfileJSTest < IntegrationTestWithJavascript click_link("amazon123 (eu-west-1-EC2)") assert page.has_selector?('.pf-c-page__main-breadcrumb .active', :text => compute_profiles(:one).name), "#{compute_profiles(:one).name} was expected in the breadcrumb active, but was not found" - selected_profile = find("#s2id_compute_attribute_compute_profile_id .select2-chosen").text + selected_profile = select2_chosen_selector('compute_attribute_compute_profile_id').text assert_equal compute_profiles(:one).name, selected_profile - selected_compute = find("#s2id_compute_attribute_compute_resource_id .select2-chosen").text + selected_compute = select2_chosen_selector('compute_attribute_compute_resource_id').text assert_equal "amazon123 (eu-west-1-EC2)", selected_compute click_button('Submit') @@ -54,7 +54,7 @@ class ComputeProfileJSTest < IntegrationTestWithJavascript fill_in('compute_profile_name', :with => 'test') click_on("Submit") assert click_link(compute_resources(:ovirt).to_s) - selected_profile = find("#s2id_compute_attribute_compute_profile_id .select2-chosen").text + selected_profile = select2_chosen_selector('compute_attribute_compute_profile_id').text assert select2('hwp_small', :from => 'compute_attribute_vm_attrs_template') wait_for_ajax assert click_button("Submit") diff --git a/test/integration/host_js_test.rb b/test/integration/host_js_test.rb index 1f0b6aca8ef..65cc933845e 100644 --- a/test/integration/host_js_test.rb +++ b/test/integration/host_js_test.rb @@ -292,7 +292,8 @@ class HostJSTest < IntegrationTestWithJavascript wait_for_ajax click_on_inherit('compute_resource') select2(overridden_hostgroup.name, :from => 'host_hostgroup_id') - assert page.find('#s2id_host_compute_resource_id .select2-chosen').has_text? overridden_hostgroup.compute_resource.name + wait_for_ajax + assert page.find(select2_selector('host_compute_resource_id'), visible: false, wait: 10).ancestor('.select2-container').has_text? overridden_hostgroup.compute_resource.name end test 'choosing a hostgroup with compute resource works' do @@ -712,8 +713,7 @@ class HostJSTest < IntegrationTestWithJavascript private def subnet_and_domain_are_selected(modal, domain) - modal.assert_selector("#interfaceModal #s2id_host_interfaces_attributes_0_domain_id .select2-chosen", - text: domain.name) + assert select2_chosen_selector('host_interfaces_attributes_0_domain_id').has_text? domain.name modal.assert_selector('#interfaceModal #host_interfaces_attributes_0_subnet_id option', visible: false, count: domain.subnets.count + 1) # plus one empty diff --git a/test/integration/provisioning_template_js_test.rb b/test/integration/provisioning_template_js_test.rb index ec6b31ed773..ef7d0d4bfd9 100644 --- a/test/integration/provisioning_template_js_test.rb +++ b/test/integration/provisioning_template_js_test.rb @@ -16,14 +16,13 @@ class ProvisioningTemplateJSTest < IntegrationTestWithJavascript click_link 'Type' assert has_unchecked_field?('provisioning_template_snippet') - assert_equal template.template_kind.name, find("#s2id_provisioning_template_template_kind_id .select2-chosen").text - + assert select2_chosen_selector('provisioning_template_template_kind_id').has_text? template.template_kind.name # check the type dropdown is hidden when snippet is checked - assert has_selector?('#s2id_provisioning_template_template_kind_id') + assert has_selector?(select2_selector('provisioning_template_template_kind_id')) find_field('provisioning_template_snippet').click - assert has_no_selector?('#s2id_provisioning_template_template_kind_id') + assert has_no_selector?(select2_selector('provisioning_template_template_kind_id')) find_field('provisioning_template_snippet').click - assert has_selector?('#s2id_provisioning_template_template_kind_id') + assert has_selector?(select2_selector('provisioning_template_template_kind_id')) assert_submit_button(provisioning_templates_path) assert page.has_link? 'updated template' @@ -39,11 +38,11 @@ class ProvisioningTemplateJSTest < IntegrationTestWithJavascript assert has_checked_field?('provisioning_template_snippet') # check the type dropdown is visible when snippet is unchecked - assert has_no_selector?('#s2id_provisioning_template_template_kind_id') + assert has_no_selector?('#kind_selector') find_field('provisioning_template_snippet').click - assert has_selector?('#s2id_provisioning_template_template_kind_id') + assert has_selector?('#kind_selector') find_field('provisioning_template_snippet').click - assert has_no_selector?('#s2id_provisioning_template_template_kind_id') + assert has_no_selector?('#kind_selector') assert_submit_button(provisioning_templates_path) assert page.has_link? 'updated snippet' diff --git a/test/integration/report_template_js_test.rb b/test/integration/report_template_js_test.rb index 0b697190c3a..ed68810e2af 100644 --- a/test/integration/report_template_js_test.rb +++ b/test/integration/report_template_js_test.rb @@ -62,7 +62,7 @@ class ReportTemplateJSIntegrationTest < IntegrationTestWithJavascript output_options = ['CSV', 'JSON', 'YAML', 'HTML'] visit generate_report_template_path(template) - find('#s2id_report_template_report_format').click + find(select2_selector('report_template_report_format')).click output_options.each { |opt| assert page.has_content? opt } end @@ -71,10 +71,10 @@ class ReportTemplateJSIntegrationTest < IntegrationTestWithJavascript template = FactoryBot.create(:report_template) visit generate_report_template_path(template) - select = find('#s2id_report_template_report_format') + select = find(select2_selector('report_template_report_format'), visible: false).ancestor('.select2-container') assert select.text '' - assert select[:class].include?('select2-container-disabled'), true + assert select[:class].include?('select2-container--disabled'), true end test "should have correct generate_at field" do diff --git a/test/integration/shared/host_finders.rb b/test/integration/shared/host_finders.rb index d8b45c403fe..61a583113ae 100644 --- a/test/integration/shared/host_finders.rb +++ b/test/integration/shared/host_finders.rb @@ -54,7 +54,7 @@ def multiple_actions_div end def click_on_inherit(attribute, prefix: 'host_') - find("##{prefix}#{attribute}_id + .input-group-btn .btn").click + find("##{prefix}#{attribute}_id").ancestor('.input-group').find(".input-group-btn .btn").click end def current_hosts_path(*args) diff --git a/test/integration/user_test.rb b/test/integration/user_test.rb index 077938b3fa9..900ad505a62 100644 --- a/test/integration/user_test.rb +++ b/test/integration/user_test.rb @@ -26,8 +26,8 @@ class UserIntegrationTest < IntegrationTestWithJavascript visit users_path click_link "Create User" assert fill_in "user_login", :with => "new_user" - find("#s2id_user_auth_source_id").click - all(".select2-result-label").find do |result| + find(select2_selector('user_auth_source_id'), visible: false).ancestor('.select2-container').click + all(".select2-results__option").find do |result| result.text == 'INTERNAL' end.click assert click_button("Submit") diff --git a/test/integration_test_helper.rb b/test/integration_test_helper.rb index fd3c0ee6888..df472c73a7b 100644 --- a/test/integration_test_helper.rb +++ b/test/integration_test_helper.rb @@ -98,17 +98,29 @@ def fix_mismatches Organization.all_import_missing_ids end + def select2_selector(name) + "#select2-#{name}-container" + end + + def select2_result_selector(name) + "#select2-#{name}-container .select2-results" + end + + def select2_chosen_selector(name) + page.find(select2_selector(name), visible: false, wait: 10).ancestor('.select2-container') + end + def select2(value, attrs) - find("#s2id_#{attrs[:from]}").click - wait_for { find('.select2-input').visible? rescue false } - wait_for { find(".select2-input").set(value) } + find(select2_selector(attrs[:from]), visible: false).ancestor('.select2-container').click + wait_for { find('.select2-search__field').visible? rescue false } + wait_for { find(".select2-search__field").set(value) } wait_for { find('.select2-results').visible? rescue false } - within ".select2-results" do - wait_for { find(".select2-results span", text: value).visible? rescue false } - find("span", text: value).click + within ".select2-results__options" do + wait_for { find(".select2-results__options li", text: value).visible? rescue false } + find("li", text: value).click end wait_for do - page.find("#s2id_#{attrs[:from]} .select2-chosen").has_text? value + select2_chosen_selector(attrs[:from]).has_text? value end end diff --git a/webpack/assets/javascripts/foreman_auth_source.js b/webpack/assets/javascripts/foreman_auth_source.js index 285ab0546d0..b8e0a2ce952 100644 --- a/webpack/assets/javascripts/foreman_auth_source.js +++ b/webpack/assets/javascripts/foreman_auth_source.js @@ -25,7 +25,7 @@ export function testConnection(item, url) { notify({ message, type: 'success' }); }, error({ responseText }) { - const error = $.parseJSON(responseText).message; + const error = JSON.parse(responseText).message; notify({ message: error, type: 'danger' }); }, complete(result) { diff --git a/webpack/assets/javascripts/foreman_http_proxies.js b/webpack/assets/javascripts/foreman_http_proxies.js index b07c6adae9d..2f8da6e7655 100644 --- a/webpack/assets/javascripts/foreman_http_proxies.js +++ b/webpack/assets/javascripts/foreman_http_proxies.js @@ -21,7 +21,7 @@ export function testConnection(item, url) { notify({ message: result.message, type: 'success' }); }, error(xhr) { - const error = $.parseJSON(xhr.responseText).message; + const error = JSON.parse(xhr.responseText).message; notify({ message: error, type: 'danger' }); }, diff --git a/webpack/assets/javascripts/foreman_tools.js b/webpack/assets/javascripts/foreman_tools.js index 5cb10eccc6f..3868bff3ea3 100644 --- a/webpack/assets/javascripts/foreman_tools.js +++ b/webpack/assets/javascripts/foreman_tools.js @@ -123,16 +123,19 @@ export function highlightTabErrors() { errorFields.parents('.tab-pane').each(function fn() { $(`a[href="#${this.id}"]`).addClass('tab-error'); }); - $('.tab-error') - .first() - .click(); - $('.nav-pills .tab-error') - .first() - .click(); + const firstTabError = document.querySelector('.tab-error'); + if (firstTabError) { + $(firstTabError).tab('show'); + } + const firstNestedTabError = document.querySelector('.nav-pills .tab-error'); + if (firstNestedTabError) { + $(firstNestedTabError).tab('show'); + } + errorFields .first() .find('.form-control') - .focus(); + .trigger('focus'); } export const loadPluginModule = async (url, scope, module, plugin = true) => { diff --git a/webpack/assets/javascripts/foreman_users.js b/webpack/assets/javascripts/foreman_users.js index d4b6a42b9a5..2da22ae8441 100644 --- a/webpack/assets/javascripts/foreman_users.js +++ b/webpack/assets/javascripts/foreman_users.js @@ -1,3 +1,4 @@ +/* eslint-disable jquery/no-trigger */ /* eslint-disable jquery/no-toggle */ /* eslint-disable jquery/no-ajax */ /* eslint-disable jquery/no-hide */ @@ -11,7 +12,7 @@ import { notify } from './foreman_toast_notifications'; export function initInheritedRoles() { $('#inherited-roles .dropdown-menu a') - .click(({ target }) => { + .on('click', ({ target }) => { $('#roles_tab li').hide(); $(`#roles_tab li[data-id = '${target.getAttribute('data-id')}']`).show(); $(target) @@ -20,7 +21,7 @@ export function initInheritedRoles() { .html(`${escape(target.text)} `); }) .first() - .click(); + .trigger('click'); } function getSelectValues({ options = [] }) { diff --git a/webpack/assets/javascripts/hosts/tableCheckboxes.js b/webpack/assets/javascripts/hosts/tableCheckboxes.js index 56121c4ff69..d360d048166 100644 --- a/webpack/assets/javascripts/hosts/tableCheckboxes.js +++ b/webpack/assets/javascripts/hosts/tableCheckboxes.js @@ -64,7 +64,7 @@ function rmHostId(id) { function readFromCookie() { try { const r = Cookies.get(cookieName); - if (r) return $.parseJSON(r); + if (r) return JSON.parse(r); return []; } catch (err) { removeForemanHostsCookie();