From 004355bd8b0374d6f41d71222601d6c597b26136 Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Sun, 5 May 2024 17:40:20 -0400 Subject: [PATCH] Implement `nokogiri-html5-inference` to properly support HTML fragment parsing (#696) This pull request implements the [`nokogiri-html5-inference`](https://github.com/flavorjones/nokogiri-html5-inference) to deal with parsing HTML5 fragments to be used with Selector Morphs. The `nokogiri-html5-inference` gem come about out of the discussions in #652 and https://github.com/sparklemotion/nokogiri/issues/3023. Fixes #652, Resolves https://github.com/stimulusreflex/stimulus_reflex/pull/692, Resolves https://github.com/stimulusreflex/stimulus_reflex/pull/674 Big thanks to @flavorjones to talking and working this through with me! ## Why should this be added This pull request makes the handling of document fragments for selector morphs more natural/predictable and resolves the last open issues to release the final versions of StimulusReflex 3.5. --- Gemfile.lock | 3 + lib/stimulus_reflex.rb | 1 + lib/stimulus_reflex/html/document_fragment.rb | 8 +- stimulus_reflex.gemspec | 1 + .../broadcasters/selector_broadcaster_test.rb | 4 +- test/html/document_fragment_test.rb | 705 +++++++++++++++--- test/html/document_test.rb | 31 + 7 files changed, 647 insertions(+), 106 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c82a4fb1..2580f44b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,7 @@ PATH activesupport (>= 5.2, < 8) cable_ready (~> 5.0) nokogiri (~> 1.0) + nokogiri-html5-inference (~> 0.3) rack (>= 2, < 4) railties (>= 5.2, < 8) redis (>= 4.0, < 6.0) @@ -127,6 +128,8 @@ GEM racc (~> 1.4) nokogiri (1.16.2-x86_64-linux) racc (~> 1.4) + nokogiri-html5-inference (0.3.0) + nokogiri (~> 1.14) parallel (1.22.1) parser (3.2.1.0) ast (~> 2.4.1) diff --git a/lib/stimulus_reflex.rb b/lib/stimulus_reflex.rb index 8f75bc5a..50c27f6d 100644 --- a/lib/stimulus_reflex.rb +++ b/lib/stimulus_reflex.rb @@ -9,6 +9,7 @@ require "action_cable" require "action_view" require "nokogiri" +require "nokogiri/html5/inference" require "cable_ready" require "stimulus_reflex/version" require "stimulus_reflex/open_struct_fix" diff --git a/lib/stimulus_reflex/html/document_fragment.rb b/lib/stimulus_reflex/html/document_fragment.rb index 7ad6879f..846e5dda 100644 --- a/lib/stimulus_reflex/html/document_fragment.rb +++ b/lib/stimulus_reflex/html/document_fragment.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true -# forzen_string_literal: true - module StimulusReflex module HTML class DocumentFragment < Document def parsing_class - Nokogiri + Nokogiri::HTML5::Inference + end + + def document_element + @document end end end diff --git a/stimulus_reflex.gemspec b/stimulus_reflex.gemspec index 5aed3d02..d2662980 100644 --- a/stimulus_reflex.gemspec +++ b/stimulus_reflex.gemspec @@ -50,6 +50,7 @@ Gem::Specification.new do |gem| gem.add_dependency "nokogiri", "~> 1.0" gem.add_dependency "rack", ">= 2", "< 4" gem.add_dependency "redis", ">= 4.0", "< 6.0" + gem.add_dependency "nokogiri-html5-inference", "~> 0.3" gem.add_development_dependency "bundler", "~> 2.0" gem.add_development_dependency "magic_frozen_string_literal", "~> 1.2" diff --git a/test/broadcasters/selector_broadcaster_test.rb b/test/broadcasters/selector_broadcaster_test.rb index ded2b4e8..bcb54a79 100644 --- a/test/broadcasters/selector_broadcaster_test.rb +++ b/test/broadcasters/selector_broadcaster_test.rb @@ -71,9 +71,7 @@ class SelectorBroadcasterTest < StimulusReflex::BroadcasterTestCase "operations" => [ { "selector" => "html", - # Nokogiri automatically adds a `` tag for the encoding - # See. https://github.com/sparklemotion/nokogiri/blob/6ea1449926ce97648bb2f7401c9e4fdcb0e261ba/lib/nokogiri/html4/document.rb#L34-L35 - "html" => "Test
bar
baz
", + "html" => "Test
bar
baz
", "payload" => {}, "childrenOnly" => true, "permanentAttributeName" => nil, diff --git a/test/html/document_fragment_test.rb b/test/html/document_fragment_test.rb index d386fbc9..92f3cb67 100644 --- a/test/html/document_fragment_test.rb +++ b/test/html/document_fragment_test.rb @@ -35,6 +35,599 @@ class StimulusReflex::HTML::DocumentFragmentTest < ActiveSupport::TestCase assert_nil fragment.match("body").inner_html end + test "should handle string" do + fragment = StimulusReflex::HTML::DocumentFragment.new("Some String") + + assert_equal "Some String", fragment.to_html + assert_equal "Some String", fragment.outer_html + assert_equal "", fragment.inner_html + + assert_nil fragment.match("html").to_html + assert_nil fragment.match("html").outer_html + assert_nil fragment.match("html").inner_html + + assert_nil fragment.match("body").to_html + assert_nil fragment.match("body").outer_html + assert_nil fragment.match("body").inner_html + end + + test "should handle number" do + fragment = StimulusReflex::HTML::DocumentFragment.new(123) + + assert_equal "123", fragment.to_html + assert_equal "123", fragment.outer_html + assert_equal "", fragment.inner_html + + assert_nil fragment.match("html").to_html + assert_nil fragment.match("html").outer_html + assert_nil fragment.match("html").inner_html + + assert_nil fragment.match("body").to_html + assert_nil fragment.match("body").outer_html + assert_nil fragment.match("body").inner_html + end + + test "should properly handle a tr without the parent table" do + html = "12" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal html, fragment.to_html.squish + end + + test "should properly handle a td without the parent table or td" do + html = "1" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal html, fragment.to_html.squish + end + + test "should properly parse " do + html = %(12) + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal %(12), fragment.to_html.squish + assert_equal %(12), fragment.outer_html.squish + assert_equal %(12), fragment.inner_html.squish + end + + test "should properly parse " do + html = "1" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "1", fragment.to_html.squish + assert_equal "1", fragment.outer_html.squish + assert_equal "1", fragment.inner_html.squish + end + + test "should properly parse " do + html = "1" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "1", fragment.to_html.squish + assert_equal "1", fragment.outer_html.squish + assert_equal "1", fragment.inner_html.squish + end + + test "should properly parse " do + html = "
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
", fragment.to_html.squish + assert_equal "
", fragment.outer_html.squish + end + + test "should properly parse with and with " do + html = "" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "", fragment.to_html.squish + assert_equal "", fragment.outer_html.squish + end + + test "should properly parse
" do + html = "
Caption
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
Caption
", fragment.to_html.squish + assert_equal "
Caption
", fragment.outer_html.squish + end + + test "should properly parse with and " do + html = "
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
", fragment.to_html.squish + assert_equal "
", fragment.outer_html.squish + end + + test "should properly parse with and and s" do + html = "
1
1
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
1
1
", fragment.to_html.squish + assert_equal "
1
1
", fragment.outer_html.squish + end + + test "should properly parse
1
1
1
1
1
1
with " do + html = "" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "", fragment.to_html.squish + assert_equal "", fragment.outer_html.squish + end + + test "should properly parse " do + html = "" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "", fragment.to_html.squish + assert_equal "", fragment.outer_html.squish + end + + test "should properly parse " do + html = "" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "", fragment.to_html.squish + assert_equal "", fragment.outer_html.squish + end + + test "should properly parse " + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "", fragment.to_html.squish + assert_equal "", fragment.outer_html.squish + end + + test "should properly parse and " do + html = %( + + + + + + ) + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal %(), fragment.to_html.squish + assert_equal %(), fragment.outer_html.squish + end + + test "should properly parse " do + html = %( + + ) + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal %(), fragment.to_html.squish + assert_equal %(), fragment.outer_html.squish + end + + test "should properly parse
" do + html = "
12
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
12
", fragment.to_html.squish + assert_equal "
12
", fragment.outer_html.squish + end + + test "should properly parse with " do + html = "
12
" + fragment = StimulusReflex::HTML::DocumentFragment.new(html) + + assert_equal "
12
", fragment.to_html.squish + assert_equal "
12
", fragment.outer_html.squish + end + + test "should properly parse
12
12
12
12
12
12
12
12
12
" do + html = "CaptionCaptionCaption