ckeditor5 1.19.5 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,7 @@
3
3
  require 'capybara'
4
4
  require 'capybara/rspec'
5
5
  require 'capybara/cuprite'
6
+ require 'rspec/retry'
6
7
 
7
8
  ENV['RAILS_ENV'] ||= 'test'
8
9
 
@@ -40,16 +41,10 @@ Capybara.server = :webrick
40
41
  Capybara.default_driver = :cuprite
41
42
  Capybara.javascript_driver = :cuprite
42
43
 
43
- require 'rspec/retry'
44
-
45
44
  RSpec.configure do |config|
46
- config.verbose_retry = true
47
-
48
45
  config.around :each, :js do |example|
49
- example.run_with_retry retry: 3, retry_wait: 5
46
+ example.run_with_retry retry: 4, retry_wait: 5, default_sleep_interval: 2
50
47
  end
51
-
52
- config.profile_examples = 10
53
48
  end
54
49
 
55
50
  Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
@@ -42,6 +42,13 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
42
42
  })
43
43
  end
44
44
 
45
+ it 'should not include importmap if not requested' do
46
+ serializer = described_class.new(bundle, importmap: false)
47
+ html = serializer.to_html
48
+
49
+ expect(html).not_to have_tag('script', with: { type: 'importmap' })
50
+ end
51
+
45
52
  it 'includes import map' do
46
53
  expect(html).to have_tag('script', with: { type: 'importmap' }) do
47
54
  with_text(%r{"@ckeditor/script2":"https://cdn\.com/script2\.js"})
@@ -131,13 +138,6 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
131
138
  nonce: 'true'
132
139
  })
133
140
  end
134
-
135
- it 'memoizes scripts import map' do
136
- first_call = serializer.send(:scripts_import_map_tag)
137
- second_call = serializer.send(:scripts_import_map_tag)
138
-
139
- expect(first_call.object_id).to eq(second_call.object_id)
140
- end
141
141
  end
142
142
 
143
143
  describe '.url_resource_preload_type' do
@@ -153,17 +153,60 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
153
153
  expect(described_class.url_resource_preload_type('file.unknown')).to eq('fetch')
154
154
  end
155
155
  end
156
+ end
156
157
 
157
- describe '#looks_like_url? (private)' do
158
- subject(:serializer) { described_class.new(CKEditor5::Rails::Assets::AssetsBundle.new) }
158
+ RSpec.describe CKEditor5::Rails::Assets::AssetsImportMap do
159
+ let(:scripts) do
160
+ [
161
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
162
+ 'https://cdn.com/script1.js',
163
+ window_name: 'WindowScript'
164
+ ),
165
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
166
+ 'https://cdn.com/script2.js',
167
+ import_name: '@ckeditor/module'
168
+ ),
169
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
170
+ 'https://cdn.com/script3.js',
171
+ import_name: 'https://example.com/module'
172
+ )
173
+ ]
174
+ end
175
+
176
+ let(:bundle) { CKEditor5::Rails::Assets::AssetsBundle.new(scripts: scripts) }
177
+ subject(:import_map) { described_class.new(bundle) }
178
+
179
+ describe '#to_json' do
180
+ it 'includes only ESM scripts without URL-like imports' do
181
+ json = JSON.parse(import_map.to_json)
182
+ expect(json['imports']).to eq({
183
+ '@ckeditor/module' => 'https://cdn.com/script2.js'
184
+ })
185
+ end
186
+ end
187
+
188
+ describe '#to_html' do
189
+ it 'generates script tag with import map' do
190
+ html = import_map.to_html
191
+ expect(html).to have_tag('script', with: { type: 'importmap', nonce: 'true' }) do
192
+ with_text('{"imports":{"@ckeditor/module":"https://cdn.com/script2.js"}}')
193
+ end
194
+ end
195
+ end
159
196
 
197
+ describe '#looks_like_url? (private)' do
160
198
  it 'returns false for invalid URIs' do
161
- expect(serializer.send(:looks_like_url?, '@ckeditor/foo')).to be false
162
- expect(serializer.send(:looks_like_url?, 'http')).to be false
163
- expect(serializer.send(:looks_like_url?, 'invalid')).to be false
164
- expect(serializer.send(:looks_like_url?, 'http://[invalid')).to be false
165
- expect(serializer.send(:looks_like_url?, "http://example.com\nmalicious")).to be false
166
- expect(serializer.send(:looks_like_url?, 'http://<invalid>')).to be false
199
+ expect(import_map.send(:looks_like_url?, '@ckeditor/foo')).to be false
200
+ expect(import_map.send(:looks_like_url?, 'http')).to be false
201
+ expect(import_map.send(:looks_like_url?, 'invalid')).to be false
202
+ expect(import_map.send(:looks_like_url?, 'http://[invalid')).to be false
203
+ expect(import_map.send(:looks_like_url?, "http://example.com\nmalicious")).to be false
204
+ expect(import_map.send(:looks_like_url?, 'http://<invalid>')).to be false
205
+ end
206
+
207
+ it 'returns true for valid HTTP(S) URLs' do
208
+ expect(import_map.send(:looks_like_url?, 'http://example.com')).to be true
209
+ expect(import_map.send(:looks_like_url?, 'https://cdn.com/script.js')).to be true
167
210
  end
168
211
  end
169
212
  end
@@ -3,7 +3,16 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe CKEditor5::Rails::Cdn::Helpers do
6
- let(:test_class) { Class.new { include CKEditor5::Rails::Cdn::Helpers } }
6
+ let(:test_class) do
7
+ Class.new do
8
+ include CKEditor5::Rails::Cdn::Helpers
9
+
10
+ def importmap_rendered?
11
+ false
12
+ end
13
+ end
14
+ end
15
+
7
16
  let(:helper) { test_class.new }
8
17
  let(:preset) do
9
18
  CKEditor5::Rails::Presets::PresetBuilder.new do
@@ -32,10 +41,6 @@ RSpec.describe CKEditor5::Rails::Cdn::Helpers do
32
41
 
33
42
  describe '#ckeditor5_assets' do
34
43
  context 'with valid preset' do
35
- it 'returns serialized bundle html' do
36
- expect(helper.ckeditor5_assets(preset: :default)).to eq(bundle_html)
37
- end
38
-
39
44
  it 'creates base bundle' do
40
45
  expect(CKEditor5::Rails::Cdn::CKEditorBundle).to receive(:new)
41
46
  .with(
@@ -218,6 +223,37 @@ RSpec.describe CKEditor5::Rails::Cdn::Helpers do
218
223
  .to raise_error(ArgumentError, /forgot to define version/)
219
224
  end
220
225
  end
226
+
227
+ context 'when Rails.application.importmap is defined' do
228
+ before do
229
+ allow(helper).to receive(:importmap_available?).and_return(true)
230
+ allow(helper).to receive(:importmap_rendered?).and_return(false)
231
+ end
232
+
233
+ it 'returns nil and stores html tags in context' do
234
+ result = helper.ckeditor5_assets(preset: :default)
235
+ expect(result).to be_nil
236
+ expect(context[:html_tags]).to eq(bundle_html)
237
+ end
238
+
239
+ it 'raise exception if importmap_rendered?' do
240
+ allow(helper).to receive(:importmap_rendered?).and_return(true)
241
+ expect { helper.ckeditor5_assets(preset: :default) }
242
+ .to raise_error(CKEditor5::Rails::Cdn::Helpers::ImportmapAlreadyRenderedError)
243
+ end
244
+ end
245
+
246
+ context 'when importmap_available? is true returns html' do
247
+ before do
248
+ allow(helper).to receive(:importmap_available?).and_return(nil)
249
+ end
250
+
251
+ it 'returns html directly' do
252
+ result = helper.ckeditor5_assets(preset: :default)
253
+ expect(result).to eq(bundle_html)
254
+ expect(context[:html_tags]).to be_nil
255
+ end
256
+ end
221
257
  end
222
258
 
223
259
  describe 'cdn helper methods' do
@@ -102,5 +102,37 @@ RSpec.describe CKEditor5::Rails::Engine do
102
102
  end
103
103
  end
104
104
  end
105
+
106
+ describe 'importmap initializer' do
107
+ context 'when Importmap is defined' do
108
+ module Importmap # rubocop:disable Lint/ConstantDefinitionInBlock
109
+ module ImportmapTagsHelper; end
110
+ end
111
+
112
+ it 'prepends CKEditor5 importmap helper' do
113
+ initializer = described_class.initializers.find { |i| i.name == 'ckeditor5.importmap' }
114
+ initializer.run(Rails.application)
115
+
116
+ expect(Importmap::ImportmapTagsHelper.ancestors)
117
+ .to include(CKEditor5::Rails::Hooks::Importmap::ImportmapTagsHelper)
118
+ end
119
+ end
120
+
121
+ context 'when Importmap is not defined' do
122
+ before do
123
+ @importmap = Importmap if defined?(Importmap)
124
+ Object.send(:remove_const, :Importmap) if defined?(Importmap)
125
+ end
126
+
127
+ after do
128
+ Object.const_set(:Importmap, @importmap) if @importmap
129
+ end
130
+
131
+ it 'does not raise error' do
132
+ initializer = described_class.initializers.find { |i| i.name == 'ckeditor5.importmap' }
133
+ expect { initializer.run(Rails.application) }.not_to raise_error
134
+ end
135
+ end
136
+ end
105
137
  end
106
138
  end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Hooks::Importmap::ImportmapTagsHelper do
6
+ let(:test_class) do
7
+ Class.new do
8
+ include ActionView::Helpers::TagHelper
9
+ include CKEditor5::Rails::Hooks::Importmap::ImportmapTagsHelper
10
+
11
+ def javascript_importmap_module_preload_tags(*)
12
+ '<script type="modulepreload">preload</script>'
13
+ end
14
+
15
+ def javascript_import_module_tag(*)
16
+ '<script type="module">import</script>'
17
+ end
18
+
19
+ def javascript_inline_importmap_tag(json)
20
+ "<script type=\"importmap\">#{json}</script>"
21
+ end
22
+ end
23
+ end
24
+
25
+ let(:helper) { test_class.new }
26
+ let(:importmap) { double('Importmap', to_json: '{"imports":{}}') }
27
+
28
+ before do
29
+ allow(Rails.application).to receive(:importmap).and_return(importmap)
30
+ end
31
+
32
+ describe '#javascript_importmap_tags' do
33
+ context 'without CKEditor context' do
34
+ it 'generates basic importmap tags' do
35
+ result = helper.javascript_importmap_tags
36
+
37
+ expect(result).to include('modulepreload')
38
+ expect(result).to include('module')
39
+ expect(helper).to be_importmap_rendered
40
+ end
41
+ end
42
+
43
+ context 'with CKEditor context' do
44
+ let(:bundle) do
45
+ CKEditor5::Rails::Assets::AssetsBundle.new(
46
+ scripts: [
47
+ CKEditor5::Rails::Assets::JSUrlImportMeta.new(
48
+ 'https://cdn.com/script.js',
49
+ import_name: '@ckeditor/module'
50
+ )
51
+ ]
52
+ )
53
+ end
54
+
55
+ let(:html_tags) { '<script src="ckeditor.js"></script>' }
56
+
57
+ before do
58
+ helper.instance_variable_set(:@__ckeditor_context, {
59
+ bundle: bundle,
60
+ html_tags: html_tags
61
+ })
62
+ end
63
+
64
+ it 'merges CKEditor importmap with base importmap' do
65
+ result = CGI.unescapeHTML(helper.javascript_importmap_tags)
66
+
67
+ expect(result).to include('@ckeditor/module')
68
+ expect(result).to include('https://cdn.com/script.js')
69
+ expect(result).to include(html_tags)
70
+ end
71
+
72
+ it 'handles invalid JSON gracefully' do
73
+ allow(importmap).to receive(:to_json).and_return('invalid json')
74
+
75
+ expect(Rails.logger).to receive(:error).with(/Failed to merge import maps/)
76
+ helper.javascript_importmap_tags
77
+ end
78
+
79
+ it 'processes importmap in correct order when context is present' do
80
+ html = CGI.unescapeHTML(helper.javascript_importmap_tags)
81
+ doc = Nokogiri::HTML::DocumentFragment.parse(html)
82
+ scripts = doc.css('script')
83
+
84
+ expect(scripts[0]['type']).to eq('importmap')
85
+ expect(scripts[1]['type']).to eq('modulepreload')
86
+ expect(scripts[2]['type']).to eq('module')
87
+ expect(scripts[3]['src']).to eq('ckeditor.js')
88
+ end
89
+
90
+ it 'adds inline importmap when context is missing' do
91
+ helper.instance_variable_set(:@__ckeditor_context, nil)
92
+ html = CGI.unescapeHTML(helper.javascript_importmap_tags)
93
+ doc = Nokogiri::HTML::DocumentFragment.parse(html)
94
+ scripts = doc.css('script')
95
+
96
+ expect(scripts[0]['type']).to eq('importmap')
97
+ expect(doc.css('script[src="ckeditor.js"]')).to be_empty
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '#importmap_rendered?' do
103
+ it 'returns false by default' do
104
+ expect(helper).not_to be_importmap_rendered
105
+ end
106
+
107
+ it 'returns true after rendering importmap' do
108
+ helper.javascript_importmap_tags
109
+ expect(helper).to be_importmap_rendered
110
+ end
111
+ end
112
+
113
+ describe '#merge_import_maps_json (private)' do
114
+ it 'correctly merges two valid import maps' do
115
+ map_a = '{"imports":{"a":"1"}}'
116
+ map_b = '{"imports":{"b":"2"}}'
117
+
118
+ result = JSON.parse(helper.send(:merge_import_maps_json, map_a, map_b))
119
+ expect(result['imports']).to eq('a' => '1', 'b' => '2')
120
+ end
121
+
122
+ it 'returns second map when first is invalid' do
123
+ map_a = 'invalid json'
124
+ map_b = '{"imports":{"b":"2"}}'
125
+
126
+ expect(Rails.logger).to receive(:error).with(/Failed to merge import maps/)
127
+ result = helper.send(:merge_import_maps_json, map_a, map_b)
128
+ expect(result).to eq(map_b)
129
+ end
130
+ end
131
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.5
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-29 00:00:00.000000000 Z
12
+ date: 2024-12-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -70,6 +70,7 @@ files:
70
70
  - lib/ckeditor5/rails/engine.rb
71
71
  - lib/ckeditor5/rails/helpers.rb
72
72
  - lib/ckeditor5/rails/hooks/form.rb
73
+ - lib/ckeditor5/rails/hooks/importmap.rb
73
74
  - lib/ckeditor5/rails/hooks/simple_form.rb
74
75
  - lib/ckeditor5/rails/plugins/simple_upload_adapter.rb
75
76
  - lib/ckeditor5/rails/plugins/wproofreader.rb
@@ -89,7 +90,7 @@ files:
89
90
  - spec/e2e/spec_helper.rb
90
91
  - spec/e2e/support/eventually.rb
91
92
  - spec/e2e/support/form_helpers.rb
92
- - spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb
93
+ - spec/lib/ckeditor5/rails/assets/assets_bundle_hml_serializer_spec.rb
93
94
  - spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb
94
95
  - spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb
95
96
  - spec/lib/ckeditor5/rails/cdn/ckeditor_bundle_spec.rb
@@ -108,6 +109,7 @@ files:
108
109
  - spec/lib/ckeditor5/rails/editor/props_spec.rb
109
110
  - spec/lib/ckeditor5/rails/engine_spec.rb
110
111
  - spec/lib/ckeditor5/rails/hooks/form_spec.rb
112
+ - spec/lib/ckeditor5/rails/hooks/importmap_spec.rb
111
113
  - spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb
112
114
  - spec/lib/ckeditor5/rails/plugins/wproofreader_spec.rb
113
115
  - spec/lib/ckeditor5/rails/presets/manager_spec.rb
@@ -154,7 +156,7 @@ test_files:
154
156
  - spec/e2e/spec_helper.rb
155
157
  - spec/e2e/support/eventually.rb
156
158
  - spec/e2e/support/form_helpers.rb
157
- - spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb
159
+ - spec/lib/ckeditor5/rails/assets/assets_bundle_hml_serializer_spec.rb
158
160
  - spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb
159
161
  - spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb
160
162
  - spec/lib/ckeditor5/rails/cdn/ckeditor_bundle_spec.rb
@@ -173,6 +175,7 @@ test_files:
173
175
  - spec/lib/ckeditor5/rails/editor/props_spec.rb
174
176
  - spec/lib/ckeditor5/rails/engine_spec.rb
175
177
  - spec/lib/ckeditor5/rails/hooks/form_spec.rb
178
+ - spec/lib/ckeditor5/rails/hooks/importmap_spec.rb
176
179
  - spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb
177
180
  - spec/lib/ckeditor5/rails/plugins/wproofreader_spec.rb
178
181
  - spec/lib/ckeditor5/rails/presets/manager_spec.rb