diff --git a/.gitignore b/.gitignore index b651e9174..6c87fa9ec 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ ext/widget_renderer/Makefile ext/widget_renderer/*.dylib # Keep the prebuilt Linux .so for Cloud Foundry deployment !ext/widget_renderer/libwidget_renderer.so + +# Certificate files (avoid accidental commits of sensitive keys/certs) +*.pem diff --git a/app/models/form.rb b/app/models/form.rb index edffcb94e..ea8aeb30b 100644 --- a/app/models/form.rb +++ b/app/models/form.rb @@ -358,28 +358,14 @@ def touchpoints_js_string end # Always use ERB template rendering for now to avoid Rust compilation issues - controller = ApplicationController.new - - # Set up a mock request with default URL options to avoid "undefined method 'host' for nil" errors - # This is necessary because the ERB templates use root_url which requires request context - # Try action_controller first, fall back to action_mailer if not set - default_options = Rails.application.config.action_controller.default_url_options || - Rails.application.config.action_mailer.default_url_options || - {} - host = default_options[:host] || 'localhost' - port = default_options[:port] || 3000 - protocol = default_options[:protocol] || (port == 443 ? 'https' : 'http') - - # Create a mock request - mock_request = ActionDispatch::Request.new( - 'rack.url_scheme' => protocol, - 'HTTP_HOST' => "#{host}#{":#{port}" if port != 80 && port != 443}", - 'SERVER_NAME' => host, - 'SERVER_PORT' => port.to_s, - ) + controller_with_request = build_controller_with_mock_request + controller_with_request.render_to_string(partial: 'components/widget/fba', formats: :js, locals: { form: self }) + end - controller.request = mock_request - controller.render_to_string(partial: 'components/widget/fba', formats: :js, locals: { form: self }) + # Renders the widget CSS partial for use with the Rust widget renderer + def render_widget_css + controller_with_request = build_controller_with_mock_request + controller_with_request.render_to_string(partial: 'components/widget/widget', formats: :css, locals: { form: self }) end # Renders the widget CSS partial for use with the Rust widget renderer @@ -1076,6 +1062,31 @@ def self.forms_whose_retention_period_has_passed private + # Builds an ApplicationController instance with a mock request for rendering partials + # This is necessary because ERB templates use URL helpers which require request context + def build_controller_with_mock_request + controller = ApplicationController.new + + # Set up a mock request with default URL options + # Try action_controller first, fall back to action_mailer if not set + default_options = Rails.application.config.action_controller.default_url_options || + Rails.application.config.action_mailer.default_url_options || + {} + host = default_options[:host] || 'localhost' + port = default_options[:port] || 3000 + protocol = default_options[:protocol] || (port == 443 ? 'https' : 'http') + + mock_request = ActionDispatch::Request.new( + 'rack.url_scheme' => protocol, + 'HTTP_HOST' => "#{host}#{":#{port}" if port != 80 && port != 443}", + 'SERVER_NAME' => host, + 'SERVER_PORT' => port.to_s, + ) + + controller.request = mock_request + controller + end + def set_uuid self.uuid ||= SecureRandom.uuid self.short_uuid ||= self.uuid[0..7] diff --git a/ext/widget_renderer/extconf.rb b/ext/widget_renderer/extconf.rb index ab434ccdc..6b8fde0a5 100644 --- a/ext/widget_renderer/extconf.rb +++ b/ext/widget_renderer/extconf.rb @@ -30,8 +30,6 @@ def ensure_rust puts "Current directory: #{Dir.pwd}" puts "Using cargo executable: #{cargo_bin}" -puts "Cleaning previous build artifacts..." -system("#{cargo_bin} clean 2>&1") puts "Running cargo build --release..." system("#{cargo_bin} build --release 2>&1") or abort 'Failed to build Rust extension' diff --git a/ext/widget_renderer/src/template_renderer.rs b/ext/widget_renderer/src/template_renderer.rs index c46483adb..736df31aa 100644 --- a/ext/widget_renderer/src/template_renderer.rs +++ b/ext/widget_renderer/src/template_renderer.rs @@ -52,10 +52,18 @@ impl TemplateRenderer { "" }; - let modal_class = if form.kind == "recruitment" { - "fba-usa-modal fba-usa-modal--lg".to_string() + let modal_class = if form.load_css { + if form.kind == "recruitment" { + "fba-usa-modal fba-usa-modal--lg".to_string() + } else { + "fba-usa-modal".to_string() + } } else { - "fba-usa-modal".to_string() + if form.kind == "recruitment" { + "usa-modal usa-modal--lg".to_string() + } else { + "usa-modal".to_string() + } }; let turnstile_check = if form.enable_turnstile { @@ -859,9 +867,10 @@ function FBAform(d, N) {{ let question_params = self.render_question_params(form); let html_body = self.render_html_body(form).replace("`", "\\`"); let html_body_no_modal = self.render_html_body_no_modal(form).replace("`", "\\`"); - // Escape the CSS for JavaScript string - escape backslashes, quotes, and newlines + // Escape the CSS for JavaScript string - escape backslashes, backticks, quotes, and newlines let escaped_css = form.css .replace("\\", "\\\\") + .replace("`", "\\`") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", ""); @@ -945,8 +954,19 @@ window.touchpointForm{uuid}.init(touchpointFormOptions{uuid}); if (fbaModalElement) {{ if (fbaUswds.Modal) fbaUswds.Modal.on(fbaModalElement); }} - // Ensure the custom button is also initialized if it exists - const customButtonEl = document.getElementById('{element_selector}'); + // Ensure the modal button is also initialized if it exists (for 'modal' delivery method) + const fbaButton = document.querySelector('#fba-button'); + if (fbaButton) {{ + if (fbaUswds.Modal) {{ + fbaUswds.Modal.on(fbaButton); + fbaButton.classList.add('fba-initialized'); + }} else {{ + console.error("Touchpoints Error: fbaUswds.Modal is not defined"); + }} + }} + // Ensure the custom button is also initialized if it exists (for 'custom-button-modal' delivery method) + const customButtonSelector = '{element_selector}'; + const customButtonEl = (customButtonSelector && customButtonSelector.length > 0) ? document.getElementById(customButtonSelector) : null; if (customButtonEl && ('{delivery_method}' === 'custom-button-modal')) {{ if (fbaUswds.Modal) {{ fbaUswds.Modal.on(customButtonEl); diff --git a/spec/features/admin/forms_spec.rb b/spec/features/admin/forms_spec.rb index 4dab24fa0..790429d9f 100644 --- a/spec/features/admin/forms_spec.rb +++ b/spec/features/admin/forms_spec.rb @@ -450,10 +450,11 @@ find('.survey-title-input').set('Updated Form Title') find('.survey-title-input').native.send_key :tab expect(page).to have_content('form title saved') + # Wait for AJAX save to complete before refreshing + wait_for_ajax # and persists after refresh visit questions_admin_form_path(form) wait_for_builder - wait_for_builder expect(find('.survey-title-input').value).to eq('Updated Form Title') end