actiontext5 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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"