actiontext5 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +13 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +140 -0
  7. data/LICENSE +21 -0
  8. data/README.md +1 -0
  9. data/Rakefile +27 -0
  10. data/actiontext.gemspec +29 -0
  11. data/app/helpers/action_text/content_helper.rb +30 -0
  12. data/app/helpers/action_text/tag_helper.rb +75 -0
  13. data/app/javascript/actiontext/attachment_upload.js +45 -0
  14. data/app/javascript/actiontext/index.js +11 -0
  15. data/app/models/action_text/rich_text.rb +25 -0
  16. data/app/views/action_text/attachables/_missing_attachable.html.erb +1 -0
  17. data/app/views/action_text/attachables/_remote_image.html.erb +8 -0
  18. data/app/views/action_text/attachment_galleries/_attachment_gallery.html.erb +3 -0
  19. data/app/views/action_text/content/_layout.html.erb +3 -0
  20. data/app/views/active_storage/blobs/_blob.html.erb +14 -0
  21. data/bin/test +6 -0
  22. data/bin/webpack +29 -0
  23. data/bin/webpack-dev-server +29 -0
  24. data/db/migrate/201805281641_create_action_text_tables.rb +14 -0
  25. data/lib/action_text/attachable.rb +82 -0
  26. data/lib/action_text/attachables/content_attachment.rb +38 -0
  27. data/lib/action_text/attachables/missing_attachable.rb +11 -0
  28. data/lib/action_text/attachables/remote_image.rb +46 -0
  29. data/lib/action_text/attachment.rb +103 -0
  30. data/lib/action_text/attachment_gallery.rb +65 -0
  31. data/lib/action_text/attachments/caching.rb +16 -0
  32. data/lib/action_text/attachments/minification.rb +17 -0
  33. data/lib/action_text/attachments/trix_conversion.rb +34 -0
  34. data/lib/action_text/attribute.rb +48 -0
  35. data/lib/action_text/content.rb +126 -0
  36. data/lib/action_text/engine.rb +45 -0
  37. data/lib/action_text/fragment.rb +57 -0
  38. data/lib/action_text/html_conversion.rb +24 -0
  39. data/lib/action_text/plain_text_conversion.rb +81 -0
  40. data/lib/action_text/serialization.rb +34 -0
  41. data/lib/action_text/trix_attachment.rb +92 -0
  42. data/lib/action_text/version.rb +5 -0
  43. data/lib/action_text.rb +38 -0
  44. data/lib/actiontext5.rb +1 -0
  45. data/lib/tasks/actiontext.rake +20 -0
  46. data/lib/templates/actiontext.scss +36 -0
  47. data/lib/templates/fixtures.yml +4 -0
  48. data/lib/templates/installer.rb +22 -0
  49. data/package.json +21 -0
  50. data/test/dummy/.babelrc +18 -0
  51. data/test/dummy/.postcssrc.yml +3 -0
  52. data/test/dummy/Rakefile +6 -0
  53. data/test/dummy/app/assets/config/manifest.js +3 -0
  54. data/test/dummy/app/assets/images/.keep +0 -0
  55. data/test/dummy/app/assets/stylesheets/application.css +16 -0
  56. data/test/dummy/app/assets/stylesheets/messages.css +4 -0
  57. data/test/dummy/app/assets/stylesheets/scaffold.css +80 -0
  58. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  59. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  60. data/test/dummy/app/controllers/application_controller.rb +2 -0
  61. data/test/dummy/app/controllers/concerns/.keep +0 -0
  62. data/test/dummy/app/controllers/messages_controller.rb +58 -0
  63. data/test/dummy/app/helpers/application_helper.rb +2 -0
  64. data/test/dummy/app/helpers/messages_helper.rb +2 -0
  65. data/test/dummy/app/javascript/packs/application.js +1 -0
  66. data/test/dummy/app/jobs/application_job.rb +2 -0
  67. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  68. data/test/dummy/app/models/application_record.rb +3 -0
  69. data/test/dummy/app/models/concerns/.keep +0 -0
  70. data/test/dummy/app/models/message.rb +4 -0
  71. data/test/dummy/app/models/person.rb +7 -0
  72. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  73. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  74. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  75. data/test/dummy/app/views/messages/_form.html.erb +27 -0
  76. data/test/dummy/app/views/messages/edit.html.erb +6 -0
  77. data/test/dummy/app/views/messages/index.html.erb +29 -0
  78. data/test/dummy/app/views/messages/new.html.erb +5 -0
  79. data/test/dummy/app/views/messages/show.html.erb +13 -0
  80. data/test/dummy/app/views/people/_trix_content_attachment.html.erb +3 -0
  81. data/test/dummy/bin/bundle +3 -0
  82. data/test/dummy/bin/rails +4 -0
  83. data/test/dummy/bin/rake +4 -0
  84. data/test/dummy/bin/setup +36 -0
  85. data/test/dummy/bin/update +31 -0
  86. data/test/dummy/bin/yarn +11 -0
  87. data/test/dummy/config/application.rb +19 -0
  88. data/test/dummy/config/boot.rb +5 -0
  89. data/test/dummy/config/cable.yml +10 -0
  90. data/test/dummy/config/database.yml +25 -0
  91. data/test/dummy/config/environment.rb +5 -0
  92. data/test/dummy/config/environments/development.rb +63 -0
  93. data/test/dummy/config/environments/production.rb +96 -0
  94. data/test/dummy/config/environments/test.rb +46 -0
  95. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  96. data/test/dummy/config/initializers/assets.rb +14 -0
  97. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  98. data/test/dummy/config/initializers/content_security_policy.rb +22 -0
  99. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  100. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  101. data/test/dummy/config/initializers/inflections.rb +16 -0
  102. data/test/dummy/config/initializers/mime_types.rb +4 -0
  103. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  104. data/test/dummy/config/locales/en.yml +33 -0
  105. data/test/dummy/config/puma.rb +34 -0
  106. data/test/dummy/config/routes.rb +4 -0
  107. data/test/dummy/config/spring.rb +6 -0
  108. data/test/dummy/config/storage.yml +35 -0
  109. data/test/dummy/config/webpack/development.js +3 -0
  110. data/test/dummy/config/webpack/environment.js +3 -0
  111. data/test/dummy/config/webpack/production.js +3 -0
  112. data/test/dummy/config/webpack/test.js +3 -0
  113. data/test/dummy/config/webpacker.yml +65 -0
  114. data/test/dummy/config.ru +5 -0
  115. data/test/dummy/db/migrate/20180208205311_create_messages.rb +8 -0
  116. data/test/dummy/db/migrate/20180212164506_create_active_storage_tables.active_storage.rb +26 -0
  117. data/test/dummy/db/migrate/2018052816_create_action_text_tables.rb +14 -0
  118. data/test/dummy/db/migrate/20181003185713_create_people.rb +9 -0
  119. data/test/dummy/db/schema.rb +58 -0
  120. data/test/dummy/lib/assets/.keep +0 -0
  121. data/test/dummy/log/.keep +0 -0
  122. data/test/dummy/package.json +11 -0
  123. data/test/dummy/public/404.html +67 -0
  124. data/test/dummy/public/422.html +67 -0
  125. data/test/dummy/public/500.html +66 -0
  126. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  127. data/test/dummy/public/apple-touch-icon.png +0 -0
  128. data/test/dummy/public/favicon.ico +0 -0
  129. data/test/dummy/storage/.keep +0 -0
  130. data/test/dummy/tmp/.keep +0 -0
  131. data/test/dummy/tmp/storage/.keep +0 -0
  132. data/test/dummy/yarn.lock +6071 -0
  133. data/test/fixtures/files/racecar.jpg +0 -0
  134. data/test/template/form_helper_test.rb +71 -0
  135. data/test/test_helper.rb +30 -0
  136. data/test/unit/attachment_test.rb +60 -0
  137. data/test/unit/content_test.rb +116 -0
  138. data/test/unit/model_test.rb +47 -0
  139. data/test/unit/plain_text_conversion_test.rb +94 -0
  140. data/test/unit/trix_attachment_test.rb +83 -0
  141. data/yarn.lock +11 -0
  142. 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="&#x2713;" />' \
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="&#x2713;" />' \
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="&#x2713;" />' \
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="&#x2713;" />' \
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
@@ -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"