actiontext5 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +13 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +140 -0
- data/LICENSE +21 -0
- data/README.md +1 -0
- data/Rakefile +27 -0
- data/actiontext.gemspec +29 -0
- data/app/helpers/action_text/content_helper.rb +30 -0
- data/app/helpers/action_text/tag_helper.rb +75 -0
- data/app/javascript/actiontext/attachment_upload.js +45 -0
- data/app/javascript/actiontext/index.js +11 -0
- data/app/models/action_text/rich_text.rb +25 -0
- data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
- data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
- data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
- data/app/views/action_text/content/_layout.html.erb +3 -0
- data/app/views/active_storage/blobs/_blob.html.erb +14 -0
- data/bin/test +6 -0
- data/bin/webpack +29 -0
- data/bin/webpack-dev-server +29 -0
- data/db/migrate/201805281641_create_action_text_tables.rb +14 -0
- data/lib/action_text/attachable.rb +82 -0
- data/lib/action_text/attachables/content_attachment.rb +38 -0
- data/lib/action_text/attachables/missing_attachable.rb +11 -0
- data/lib/action_text/attachables/remote_image.rb +46 -0
- data/lib/action_text/attachment.rb +103 -0
- data/lib/action_text/attachment_gallery.rb +65 -0
- data/lib/action_text/attachments/caching.rb +16 -0
- data/lib/action_text/attachments/minification.rb +17 -0
- data/lib/action_text/attachments/trix_conversion.rb +34 -0
- data/lib/action_text/attribute.rb +48 -0
- data/lib/action_text/content.rb +126 -0
- data/lib/action_text/engine.rb +45 -0
- data/lib/action_text/fragment.rb +57 -0
- data/lib/action_text/html_conversion.rb +24 -0
- data/lib/action_text/plain_text_conversion.rb +81 -0
- data/lib/action_text/serialization.rb +34 -0
- data/lib/action_text/trix_attachment.rb +92 -0
- data/lib/action_text/version.rb +5 -0
- data/lib/action_text.rb +38 -0
- data/lib/actiontext5.rb +1 -0
- data/lib/tasks/actiontext.rake +20 -0
- data/lib/templates/actiontext.scss +36 -0
- data/lib/templates/fixtures.yml +4 -0
- data/lib/templates/installer.rb +22 -0
- data/package.json +21 -0
- data/test/dummy/.babelrc +18 -0
- data/test/dummy/.postcssrc.yml +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +16 -0
- data/test/dummy/app/assets/stylesheets/messages.css +4 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/messages_controller.rb +58 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/messages_helper.rb +2 -0
- data/test/dummy/app/javascript/packs/application.js +1 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/message.rb +4 -0
- data/test/dummy/app/models/person.rb +7 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/app/views/messages/_form.html.erb +27 -0
- data/test/dummy/app/views/messages/edit.html.erb +6 -0
- data/test/dummy/app/views/messages/index.html.erb +29 -0
- data/test/dummy/app/views/messages/new.html.erb +5 -0
- data/test/dummy/app/views/messages/show.html.erb +13 -0
- data/test/dummy/app/views/people/_trix_content_attachment.html.erb +3 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +36 -0
- data/test/dummy/bin/update +31 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config/application.rb +19 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +63 -0
- data/test/dummy/config/environments/production.rb +96 -0
- data/test/dummy/config/environments/test.rb +46 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +22 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +35 -0
- data/test/dummy/config/webpack/development.js +3 -0
- data/test/dummy/config/webpack/environment.js +3 -0
- data/test/dummy/config/webpack/production.js +3 -0
- data/test/dummy/config/webpack/test.js +3 -0
- data/test/dummy/config/webpacker.yml +65 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/migrate/20180208205311_create_messages.rb +8 -0
- data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +26 -0
- data/test/dummy/db/migrate/2018052816_create_action_text_tables.rb +14 -0
- data/test/dummy/db/migrate/20181003185713_create_people.rb +9 -0
- data/test/dummy/db/schema.rb +58 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/package.json +11 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/storage/.keep +0 -0
- data/test/dummy/tmp/.keep +0 -0
- data/test/dummy/tmp/storage/.keep +0 -0
- data/test/dummy/yarn.lock +6071 -0
- data/test/fixtures/files/racecar.jpg +0 -0
- data/test/template/form_helper_test.rb +71 -0
- data/test/test_helper.rb +30 -0
- data/test/unit/attachment_test.rb +60 -0
- data/test/unit/content_test.rb +116 -0
- data/test/unit/model_test.rb +47 -0
- data/test/unit/plain_text_conversion_test.rb +94 -0
- data/test/unit/trix_attachment_test.rb +83 -0
- data/yarn.lock +11 -0
- metadata +372 -0
Binary file
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::FormHelperTest < ActionView::TestCase
|
6
|
+
tests ActionText::TagHelper
|
7
|
+
|
8
|
+
test "form with rich text area" do
|
9
|
+
form_with model: Message.new, scope: :message do |form|
|
10
|
+
form.rich_text_area :content
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_dom_equal \
|
14
|
+
'<form action="/messages" accept-charset="UTF-8" data-remote="true" method="post">' \
|
15
|
+
'<input name="utf8" type="hidden" value="✓" />' \
|
16
|
+
'<input type="hidden" name="message[content]" id="message_content_trix_input_message" />' \
|
17
|
+
'<trix-editor id="message_content" input="message_content_trix_input_message" class="trix-content" data-direct-upload-url="http://test.host/rails/active_storage/direct_uploads" data-blob-url-template="http://test.host/rails/active_storage/blobs/:signed_id/:filename">' \
|
18
|
+
'</trix-editor>' \
|
19
|
+
'</form>',
|
20
|
+
output_buffer
|
21
|
+
end
|
22
|
+
|
23
|
+
test "form with rich text area having class" do
|
24
|
+
form_with model: Message.new, scope: :message do |form|
|
25
|
+
form.rich_text_area :content, class: "custom-class"
|
26
|
+
end
|
27
|
+
|
28
|
+
assert_dom_equal \
|
29
|
+
'<form action="/messages" accept-charset="UTF-8" data-remote="true" method="post">' \
|
30
|
+
'<input name="utf8" type="hidden" value="✓" />' \
|
31
|
+
'<input type="hidden" name="message[content]" id="message_content_trix_input_message" />' \
|
32
|
+
'<trix-editor id="message_content" input="message_content_trix_input_message" class="custom-class" data-direct-upload-url="http://test.host/rails/active_storage/direct_uploads" data-blob-url-template="http://test.host/rails/active_storage/blobs/:signed_id/:filename">' \
|
33
|
+
'</trix-editor>' \
|
34
|
+
'</form>',
|
35
|
+
output_buffer
|
36
|
+
end
|
37
|
+
|
38
|
+
test "form with rich text area for non-attribute" do
|
39
|
+
form_with model: Message.new, scope: :message do |form|
|
40
|
+
form.rich_text_area :not_an_attribute
|
41
|
+
end
|
42
|
+
|
43
|
+
assert_dom_equal \
|
44
|
+
'<form action="/messages" accept-charset="UTF-8" data-remote="true" method="post">' \
|
45
|
+
'<input name="utf8" type="hidden" value="✓" />' \
|
46
|
+
'<input type="hidden" name="message[not_an_attribute]" id="message_not_an_attribute_trix_input_message" />' \
|
47
|
+
'<trix-editor id="message_not_an_attribute" input="message_not_an_attribute_trix_input_message" class="trix-content" data-direct-upload-url="http://test.host/rails/active_storage/direct_uploads" data-blob-url-template="http://test.host/rails/active_storage/blobs/:signed_id/:filename">' \
|
48
|
+
'</trix-editor>' \
|
49
|
+
'</form>',
|
50
|
+
output_buffer
|
51
|
+
end
|
52
|
+
|
53
|
+
test "modelless form with rich text area" do
|
54
|
+
form_with url: "/messages", scope: :message do |form|
|
55
|
+
form.rich_text_area :content
|
56
|
+
end
|
57
|
+
|
58
|
+
assert_dom_equal \
|
59
|
+
'<form action="/messages" accept-charset="UTF-8" data-remote="true" method="post">' \
|
60
|
+
'<input name="utf8" type="hidden" value="✓" />' \
|
61
|
+
'<input type="hidden" name="message[content]" id="trix_input_1" />' \
|
62
|
+
'<trix-editor id="message_content" input="trix_input_1" class="trix-content" data-direct-upload-url="http://test.host/rails/active_storage/direct_uploads" data-blob-url-template="http://test.host/rails/active_storage/blobs/:signed_id/:filename">' \
|
63
|
+
'</trix-editor>' \
|
64
|
+
'</form>',
|
65
|
+
output_buffer
|
66
|
+
end
|
67
|
+
|
68
|
+
def form_with(*)
|
69
|
+
@output_buffer = super
|
70
|
+
end
|
71
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configure Rails Environment
|
4
|
+
ENV["RAILS_ENV"] = "test"
|
5
|
+
|
6
|
+
require_relative "../test/dummy/config/environment"
|
7
|
+
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
|
8
|
+
require "rails/test_help"
|
9
|
+
|
10
|
+
# Filter out Minitest backtrace while allowing backtrace from other libraries
|
11
|
+
# to be shown.
|
12
|
+
Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
13
|
+
|
14
|
+
require "rails/test_unit/reporter"
|
15
|
+
Rails::TestUnitReporter.executable = 'bin/test'
|
16
|
+
|
17
|
+
# Load fixtures from the engine
|
18
|
+
if ActiveSupport::TestCase.respond_to?(:fixture_path=)
|
19
|
+
ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
|
20
|
+
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
|
21
|
+
ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files"
|
22
|
+
ActiveSupport::TestCase.fixtures :all
|
23
|
+
end
|
24
|
+
|
25
|
+
class ActiveSupport::TestCase
|
26
|
+
private
|
27
|
+
def create_file_blob(filename:, content_type:, metadata: nil)
|
28
|
+
ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::AttachmentTest < ActiveSupport::TestCase
|
6
|
+
test "from_attachable" do
|
7
|
+
attachment = ActionText::Attachment.from_attachable(attachable, caption: "Captioned")
|
8
|
+
assert_equal attachable, attachment.attachable
|
9
|
+
assert_equal "Captioned", attachment.caption
|
10
|
+
end
|
11
|
+
|
12
|
+
test "proxies missing methods to attachable" do
|
13
|
+
attachable.instance_eval { def proxied; "proxied"; end }
|
14
|
+
attachment = ActionText::Attachment.from_attachable(attachable)
|
15
|
+
assert_equal "proxied", attachment.proxied
|
16
|
+
end
|
17
|
+
|
18
|
+
test "proxies #to_param to attachable" do
|
19
|
+
attachment = ActionText::Attachment.from_attachable(attachable)
|
20
|
+
assert_equal attachable.to_param, attachment.to_param
|
21
|
+
end
|
22
|
+
|
23
|
+
test "converts to TrixAttachment" do
|
24
|
+
attachment = attachment_from_html(%Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>))
|
25
|
+
|
26
|
+
trix_attachment = attachment.to_trix_attachment
|
27
|
+
assert_kind_of ActionText::TrixAttachment, trix_attachment
|
28
|
+
|
29
|
+
assert_equal attachable.attachable_sgid, trix_attachment.attributes["sgid"]
|
30
|
+
assert_equal attachable.attachable_content_type, trix_attachment.attributes["contentType"]
|
31
|
+
assert_equal attachable.filename.to_s, trix_attachment.attributes["filename"]
|
32
|
+
assert_equal attachable.byte_size, trix_attachment.attributes["filesize"]
|
33
|
+
assert_equal "Captioned", trix_attachment.attributes["caption"]
|
34
|
+
|
35
|
+
assert_nil trix_attachment.attributes["content"]
|
36
|
+
end
|
37
|
+
|
38
|
+
test "converts to TrixAttachment with content" do
|
39
|
+
attachable = Person.create! name: "Javan"
|
40
|
+
attachment = attachment_from_html(%Q(<action-text-attachment sgid="#{attachable.attachable_sgid}"></action-text-attachment>))
|
41
|
+
|
42
|
+
trix_attachment = attachment.to_trix_attachment
|
43
|
+
assert_kind_of ActionText::TrixAttachment, trix_attachment
|
44
|
+
|
45
|
+
assert_equal attachable.attachable_sgid, trix_attachment.attributes["sgid"]
|
46
|
+
assert_equal attachable.attachable_content_type, trix_attachment.attributes["contentType"]
|
47
|
+
|
48
|
+
assert_not_nil attachable.to_trix_content_attachment_partial_path
|
49
|
+
assert_not_nil trix_attachment.attributes["content"]
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def attachment_from_html(html)
|
54
|
+
ActionText::Content.new(html).attachments.first
|
55
|
+
end
|
56
|
+
|
57
|
+
def attachable
|
58
|
+
@attachment ||= create_file_blob(filename: "racecar.jpg", content_type: "image/jpg")
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::ContentTest < ActiveSupport::TestCase
|
6
|
+
test "equality" do
|
7
|
+
html = %Q(<div>test</div>)
|
8
|
+
content = content_from_html(html)
|
9
|
+
assert_equal content, content_from_html(html)
|
10
|
+
assert_not_equal content, html
|
11
|
+
end
|
12
|
+
|
13
|
+
test "marshal serialization" do
|
14
|
+
content = content_from_html("Hello!")
|
15
|
+
assert_equal content, Marshal.load(Marshal.dump(content))
|
16
|
+
end
|
17
|
+
|
18
|
+
test "roundtrips HTML without additional newlines" do
|
19
|
+
html = %Q(<div>a<br></div>)
|
20
|
+
content = content_from_html(html)
|
21
|
+
assert_equal html, content.to_html
|
22
|
+
end
|
23
|
+
|
24
|
+
test "extracts links" do
|
25
|
+
html = %Q(<a href="http://example.com/1">1</a><br><a href="http://example.com/1">1</a>)
|
26
|
+
content = content_from_html(html)
|
27
|
+
assert_equal ["http://example.com/1"], content.links
|
28
|
+
end
|
29
|
+
|
30
|
+
test "extracts attachables" do
|
31
|
+
attachable = create_file_blob(filename: "racecar.jpg", content_type: "image/jpg")
|
32
|
+
html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}" caption="Captioned"></action-text-attachment>)
|
33
|
+
|
34
|
+
content = content_from_html(html)
|
35
|
+
assert_equal 1, content.attachments.size
|
36
|
+
|
37
|
+
attachment = content.attachments.first
|
38
|
+
assert_equal "Captioned", attachment.caption
|
39
|
+
assert_equal attachable, attachment.attachable
|
40
|
+
end
|
41
|
+
|
42
|
+
test "extracts remote image attachables" do
|
43
|
+
html = %Q(<action-text-attachment content-type="image" url="http://example.com/cat.jpg" width="100" height="100" caption="Captioned"></action-text-attachment>)
|
44
|
+
|
45
|
+
content = content_from_html(html)
|
46
|
+
assert_equal 1, content.attachments.size
|
47
|
+
|
48
|
+
attachment = content.attachments.first
|
49
|
+
assert_equal "Captioned", attachment.caption
|
50
|
+
|
51
|
+
attachable = attachment.attachable
|
52
|
+
assert_kind_of ActionText::Attachables::RemoteImage, attachable
|
53
|
+
assert_equal "http://example.com/cat.jpg", attachable.url
|
54
|
+
assert_equal "100", attachable.width
|
55
|
+
assert_equal "100", attachable.height
|
56
|
+
end
|
57
|
+
|
58
|
+
test "identifies destroyed attachables as missing" do
|
59
|
+
attachable = create_file_blob(filename: "racecar.jpg", content_type: "image/jpg")
|
60
|
+
html = %Q(<action-text-attachment sgid="#{attachable.attachable_sgid}"></action-text-attachment>)
|
61
|
+
attachable.destroy!
|
62
|
+
content = content_from_html(html)
|
63
|
+
assert_equal 1, content.attachments.size
|
64
|
+
assert_equal ActionText::Attachables::MissingAttachable, content.attachments.first.attachable
|
65
|
+
end
|
66
|
+
|
67
|
+
test "extracts missing attachables" do
|
68
|
+
html = %Q(<action-text-attachment sgid="missing"></action-text-attachment>)
|
69
|
+
content = content_from_html(html)
|
70
|
+
assert_equal 1, content.attachments.size
|
71
|
+
assert_equal ActionText::Attachables::MissingAttachable, content.attachments.first.attachable
|
72
|
+
end
|
73
|
+
|
74
|
+
test "converts Trix-formatted attachments" do
|
75
|
+
html = %Q(<figure data-trix-attachment='{"sgid":"123","contentType":"text/plain","width":100,"height":100}' data-trix-attributes='{"caption":"Captioned"}'></figure>)
|
76
|
+
content = content_from_html(html)
|
77
|
+
assert_equal 1, content.attachments.size
|
78
|
+
assert_equal %Q(<action-text-attachment sgid="123" content-type="text/plain" width="100" height="100" caption="Captioned"></action-text-attachment>), content.to_html
|
79
|
+
end
|
80
|
+
|
81
|
+
test "ignores Trix-formatted attachments with malformed JSON" do
|
82
|
+
html = %Q(<div data-trix-attachment='{"sgid":"garbage...'></div>)
|
83
|
+
content = content_from_html(html)
|
84
|
+
assert_equal 0, content.attachments.size
|
85
|
+
end
|
86
|
+
|
87
|
+
test "minifies attachment markup" do
|
88
|
+
html = %Q(<action-text-attachment sgid="123"><div>HTML</div></action-text-attachment>)
|
89
|
+
assert_equal %Q(<action-text-attachment sgid="123"></action-text-attachment>), content_from_html(html).to_html
|
90
|
+
end
|
91
|
+
|
92
|
+
test "canonicalizes attachment gallery markup" do
|
93
|
+
attachment_html = %Q(<action-text-attachment sgid="1" presentation="gallery"></action-text-attachment><action-text-attachment sgid="2" presentation="gallery"></action-text-attachment>)
|
94
|
+
html = %Q(<div class="attachment-gallery attachment-gallery--2">#{attachment_html}</div>)
|
95
|
+
assert_equal %Q(<div>#{attachment_html}</div>), content_from_html(html).to_html
|
96
|
+
end
|
97
|
+
|
98
|
+
test "canonicalizes attachment gallery markup with whitespace" do
|
99
|
+
attachment_html = %Q(\n <action-text-attachment sgid="1" presentation="gallery"></action-text-attachment>\n <action-text-attachment sgid="2" presentation="gallery"></action-text-attachment>\n)
|
100
|
+
html = %Q(<div class="attachment-gallery attachment-gallery--2">#{attachment_html}</div>)
|
101
|
+
assert_equal %Q(<div>#{attachment_html}</div>), content_from_html(html).to_html
|
102
|
+
end
|
103
|
+
|
104
|
+
test "canonicalizes nested attachment gallery markup" do
|
105
|
+
attachment_html = %Q(<action-text-attachment sgid="1" presentation="gallery"></action-text-attachment><action-text-attachment sgid="2" presentation="gallery"></action-text-attachment>)
|
106
|
+
html = %Q(<blockquote><div class="attachment-gallery attachment-gallery--2">#{attachment_html}</div></blockquote>)
|
107
|
+
assert_equal %Q(<blockquote><div>#{attachment_html}</div></blockquote>), content_from_html(html).to_html
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def content_from_html(html)
|
112
|
+
ActionText::Content.new(html).tap do |content|
|
113
|
+
assert_nothing_raised { content.to_s }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::ModelTest < ActiveSupport::TestCase
|
6
|
+
test "html conversion" do
|
7
|
+
message = Message.new(subject: "Greetings", content: "<h1>Hello world</h1>")
|
8
|
+
assert_equal %Q(<div class="trix-content">\n <h1>Hello world</h1>\n</div>\n), "#{message.content}"
|
9
|
+
end
|
10
|
+
|
11
|
+
test "plain text conversion" do
|
12
|
+
message = Message.new(subject: "Greetings", content: "<h1>Hello world</h1>")
|
13
|
+
assert_equal "Hello world", message.content.to_plain_text
|
14
|
+
end
|
15
|
+
|
16
|
+
test "without content" do
|
17
|
+
message = Message.create!(subject: "Greetings")
|
18
|
+
assert message.content.nil?
|
19
|
+
assert message.content.blank?
|
20
|
+
assert message.content.empty?
|
21
|
+
assert_not message.content.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
test "with blank content" do
|
25
|
+
message = Message.create!(subject: "Greetings", content: "")
|
26
|
+
assert_not message.content.nil?
|
27
|
+
assert message.content.blank?
|
28
|
+
assert message.content.empty?
|
29
|
+
assert_not message.content.present?
|
30
|
+
end
|
31
|
+
|
32
|
+
test "embed extraction" do
|
33
|
+
blob = create_file_blob(filename: "racecar.jpg", content_type: "image/jpg")
|
34
|
+
message = Message.create!(subject: "Greetings", content: ActionText::Content.new("Hello world").append_attachables(blob))
|
35
|
+
assert_equal "racecar.jpg", message.content.embeds.first.filename.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
test "saving content" do
|
39
|
+
message = Message.create!(subject: "Greetings", content: "<h1>Hello world</h1>")
|
40
|
+
assert_equal "Hello world", message.content.to_plain_text
|
41
|
+
end
|
42
|
+
|
43
|
+
test "save body" do
|
44
|
+
message = Message.create(subject: "Greetings", body: "<h1>Hello world</h1>")
|
45
|
+
assert_equal "Hello world", message.body.to_plain_text
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::PlainTextConversionTest < ActiveSupport::TestCase
|
6
|
+
test "<p> tags are separated by two new lines" do
|
7
|
+
assert_converted_to(
|
8
|
+
"Hello world!\n\nHow are you?",
|
9
|
+
"<p>Hello world!</p><p>How are you?</p>"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
test "<blockquote> tags are separated by two new lines" do
|
14
|
+
assert_converted_to(
|
15
|
+
"“Hello world!”\n\n“How are you?”",
|
16
|
+
"<blockquote>Hello world!</blockquote><blockquote>How are you?</blockquote>"
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
test "<ol> tags are separated by two new lines" do
|
21
|
+
assert_converted_to(
|
22
|
+
"Hello world!\n\n1. list1\n\n1. list2\n\nHow are you?",
|
23
|
+
"<p>Hello world!</p><ol><li>list1</li></ol><ol><li>list2</li></ol><p>How are you?</p>"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
test "<ul> tags are separated by two new lines" do
|
28
|
+
assert_converted_to(
|
29
|
+
"Hello world!\n\n• list1\n\n• list2\n\nHow are you?",
|
30
|
+
"<p>Hello world!</p><ul><li>list1</li></ul><ul><li>list2</li></ul><p>How are you?</p>"
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
test "<h1> tags are separated by two new lines" do
|
35
|
+
assert_converted_to(
|
36
|
+
"Hello world!\n\nHow are you?",
|
37
|
+
"<h1>Hello world!</h1><div>How are you?</div>"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
test "<li> tags are separated by one new line" do
|
42
|
+
assert_converted_to(
|
43
|
+
"• one\n• two\n• three",
|
44
|
+
"<ul><li>one</li><li>two</li><li>three</li></ul>"
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
test "<li> tags without a parent list" do
|
49
|
+
assert_converted_to(
|
50
|
+
"• one\n• two\n• three",
|
51
|
+
"<li>one</li><li>two</li><li>three</li>"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "<br> tags are separated by one new line" do
|
56
|
+
assert_converted_to(
|
57
|
+
"Hello world!\none\ntwo\nthree",
|
58
|
+
"<p>Hello world!<br>one<br>two<br>three</p>"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
test "<div> tags are separated by one new line" do
|
63
|
+
assert_converted_to(
|
64
|
+
"Hello world!\nHow are you?",
|
65
|
+
"<div>Hello world!</div><div>How are you?</div>"
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
test "<action-text-attachment> tags are converted to their plain-text representation" do
|
70
|
+
assert_converted_to(
|
71
|
+
"Hello world! [Cat]",
|
72
|
+
%Q(Hello world! <action-text-attachment url="http://example.com/cat.jpg" content-type="image" caption="Cat"></action-text-attachment>)
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
test "preserves non-linebreak whitespace after text" do
|
77
|
+
assert_converted_to(
|
78
|
+
"Hello world!",
|
79
|
+
%Q(<div><strong>Hello </strong>world!</div>)
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
test "preserves trailing linebreaks after text" do
|
84
|
+
assert_converted_to(
|
85
|
+
"Hello\nHow are you?",
|
86
|
+
"<strong>Hello<br></strong>How are you?"
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def assert_converted_to(plain_text, html)
|
92
|
+
assert_equal plain_text, ActionText::Content.new(html).to_plain_text
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ActionText::TrixAttachmentTest < ActiveSupport::TestCase
|
6
|
+
test "from_attributes" do
|
7
|
+
attributes = {
|
8
|
+
"data-trix-attachment" => {
|
9
|
+
"sgid" => "123",
|
10
|
+
"contentType" => "text/plain",
|
11
|
+
"href" => "http://example.com/",
|
12
|
+
"filename" => "example.txt",
|
13
|
+
"filesize" => 12345,
|
14
|
+
"previewable" => true
|
15
|
+
},
|
16
|
+
"data-trix-attributes" => {
|
17
|
+
"caption" => "hello"
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
attachment = attachment(
|
22
|
+
sgid: "123",
|
23
|
+
content_type: "text/plain",
|
24
|
+
href: "http://example.com/",
|
25
|
+
filename: "example.txt",
|
26
|
+
filesize: "12345",
|
27
|
+
previewable: "true",
|
28
|
+
caption: "hello"
|
29
|
+
)
|
30
|
+
|
31
|
+
assert_attachment_json_attributes(attachment, attributes)
|
32
|
+
end
|
33
|
+
|
34
|
+
test "previewable is typecast" do
|
35
|
+
assert_attachment_attribute(attachment(previewable: ""), "previewable", false)
|
36
|
+
assert_attachment_attribute(attachment(previewable: false), "previewable", false)
|
37
|
+
assert_attachment_attribute(attachment(previewable: "false"), "previewable", false)
|
38
|
+
assert_attachment_attribute(attachment(previewable: "garbage"), "previewable", false)
|
39
|
+
assert_attachment_attribute(attachment(previewable: true), "previewable", true)
|
40
|
+
assert_attachment_attribute(attachment(previewable: "true"), "previewable", true)
|
41
|
+
end
|
42
|
+
|
43
|
+
test "filesize is typecast when integer-like" do
|
44
|
+
assert_attachment_attribute(attachment(filesize: 123), "filesize", 123)
|
45
|
+
assert_attachment_attribute(attachment(filesize: "123"), "filesize", 123)
|
46
|
+
assert_attachment_attribute(attachment(filesize: "3.5 MB"), "filesize", "3.5 MB")
|
47
|
+
assert_attachment_attribute(attachment(filesize: nil), "filesize", nil)
|
48
|
+
assert_attachment_attribute(attachment(filesize: ""), "filesize", "")
|
49
|
+
end
|
50
|
+
|
51
|
+
test "#attributes strips unmappable attributes" do
|
52
|
+
attributes = {
|
53
|
+
"sgid" => "123",
|
54
|
+
"caption" => "hello"
|
55
|
+
}
|
56
|
+
|
57
|
+
attachment = attachment(sgid: "123", caption: "hello", nonexistent: "garbage")
|
58
|
+
assert_attachment_attributes(attachment, attributes)
|
59
|
+
end
|
60
|
+
|
61
|
+
def assert_attachment_attribute(attachment, name, value)
|
62
|
+
if value.nil?
|
63
|
+
assert_nil(attachment.attributes[name])
|
64
|
+
else
|
65
|
+
assert_equal(value, attachment.attributes[name])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def assert_attachment_attributes(attachment, attributes)
|
70
|
+
assert_equal(attributes, attachment.attributes)
|
71
|
+
end
|
72
|
+
|
73
|
+
def assert_attachment_json_attributes(attachment, attributes)
|
74
|
+
attributes.each do |name, expected|
|
75
|
+
actual = JSON.parse(attachment.node[name])
|
76
|
+
assert_equal(expected, actual)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def attachment(**attributes)
|
81
|
+
ActionText::TrixAttachment.from_attributes(attributes)
|
82
|
+
end
|
83
|
+
end
|
data/yarn.lock
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
2
|
+
# yarn lockfile v1
|
3
|
+
|
4
|
+
|
5
|
+
"activestorage@>= 5.2.0-rc1":
|
6
|
+
version "5.2.0-rc1"
|
7
|
+
resolved "https://registry.yarnpkg.com/activestorage/-/activestorage-5.2.0-rc1.tgz#79898996eceb0f13575eff41fb109051fbfa49b0"
|
8
|
+
|
9
|
+
trix@^0.11.1:
|
10
|
+
version "0.11.1"
|
11
|
+
resolved "https://registry.yarnpkg.com/trix/-/trix-0.11.1.tgz#ffe54f2757c2c2385b8424fd5c5d2ab712a09acc"
|