hotwired / turbo-rails

Use Turbo in your Ruby on Rails app
https://turbo.hotwired.dev
MIT License
2.07k stars 320 forks source link

Single quotes not properly handled in Turbo Streams broadcast #588

Open nquocvuong opened 6 months ago

nquocvuong commented 6 months ago

I have a form in my Rails application that is updated via Turbo Streams. The form contains input fields, and when certain actions occur (e.g., form submission), I use Turbo Streams to broadcast updates and refresh the form on the client side.

However, I've encountered an inconsistency in the behavior of Turbo Streams when handling single quotes (') within the HTML content being broadcast. Specifically, it appears that single quotes within the HTML content are being improperly escaped or encoded, resulting in single quotes is doubled when the form is refreshed.

screenshot:

image

this is my broadcast code:

Turbo::StreamsChannel.broadcast_replace_to(
    :documents,
    target: dom_id(document),
    partial: 'documents/document', # form
    locals: {document: document}
  )

and this is the message receive on client-side ( by websocket)

"<turbo-stream action=\"replace\" target=\"=document_b94ce5f6-c3f4-4d08-a050-3724356fdba8\"><template><turbo-frame id=\"document_b94ce5f6-c3f4-4d08-a050-3724356fdba8\">
<form action=\"/documents/b94ce5f6-c3f4-4d08-a050-3724356fdba8\" accept-charset=\"UTF-8\" method=\"post\">
<input type=''submit'' hidden />
...
<div class=\"has-error\">
  <input class=\"form-control\" type=\"date\" name=\"document[document_date]\" id=\"document_document_date\" />
  <span class=\"help-block form-text\">can''t be blank</span>
 </div>
...
krschacht commented 1 month ago

I’m having this issue as well. It took me a bit to track down the source of the issue so finding this was helpful. However, I see it’s been a few months and no acknowledgement. I’m not sure if this repo is monitored, maybe it needs to be posted to turbo core repo?

seanpdoyle commented 1 month ago

Are either of you able to share a script or code sample that reproduces this issue?

Save the following to a local file, make changes to demonstrate your use case, execute it with ruby bug.rb, then share the contents:

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "rails"
  gem "propshaft"
  gem "puma"
  gem "sqlite3", "~> 1.4"
  gem "turbo-rails"

  gem "capybara"
  gem "cuprite", require: "capybara/cuprite"
end

ENV["DATABASE_URL"] = "sqlite3::memory:"
ENV["RAILS_ENV"] = "test"

require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
require "action_view/railtie"
# require "action_mailer/railtie"
# require "active_job/railtie"
require "action_cable/engine"
# require "action_mailbox/engine"
# require "action_text/engine"
require "rails/test_unit/railtie"

class App < Rails::Application
  config.load_defaults Rails::VERSION::STRING.to_f

  config.root = __dir__
  config.hosts << "example.org"
  config.eager_load = false
  config.session_store :cookie_store, key: "cookie_store_key"
  config.secret_key_base = "secret_key_base"
  config.consider_all_requests_local = true
  config.action_cable.cable = {"adapter" => "async"}
  config.turbo.draw_routes = false

  Rails.logger = config.logger = Logger.new($stdout)

  routes.append do
    root to: "application#index"
  end
end

Rails.application.initialize!

ActiveRecord::Schema.define do
  create_table :messages, force: true do |t|
    t.text :body, null: false
  end
end

class Message < ActiveRecord::Base
end

class ApplicationController < ActionController::Base
  include Rails.application.routes.url_helpers

  class_attribute :template, default: DATA.read

  def index
    render inline: template, formats: :html
  end
end

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :cuprite, using: :chrome, screen_size: [1400, 1400], options: {js_errors: true}
end

Capybara.configure do |config|
  config.server = :puma, {Silent: true}
  config.default_normalize_ws = true
end

require "rails/test_help"

class TurboSystemTest < ApplicationSystemTestCase
  test "reproduces bug" do
    visit root_path

    assert_text "Loaded with Turbo"
  end
end

__END__
<!DOCTYPE html>
<html>
  <head>
    <%= csrf_meta_tags %>

    <script type="importmap">
      {
        "imports": {
          "@hotwired/turbo-rails": "<%= asset_path("turbo.js") %>"
        }
      }
    </script>

    <script type="module">
      import "@hotwired/turbo-rails"

      addEventListener("turbo:load", () => document.body.innerHTML = "Loaded with Turbo")
    </script>
  </head>

  <body>Loaded without Turbo</body>
</html>
krschacht commented 1 month ago

@seanpdoyle The situation in which this is occurring is pretty deep within my app. I'll try and find some time in the next couple weeks to create a simpler repro of it. For now I fixed it with a front-end hack replacing '' with '