ckeditor5 1.15.7 → 1.15.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -5
  3. data/README.md +3 -0
  4. data/lib/ckeditor5/rails/cdn/ckbox_bundle.rb +2 -2
  5. data/lib/ckeditor5/rails/cdn/helpers.rb +6 -0
  6. data/lib/ckeditor5/rails/context/helpers.rb +1 -1
  7. data/lib/ckeditor5/rails/editor/editable_height_normalizer.rb +50 -0
  8. data/lib/ckeditor5/rails/editor/helpers/config_helpers.rb +3 -3
  9. data/lib/ckeditor5/rails/editor/helpers/editor_helpers.rb +5 -4
  10. data/lib/ckeditor5/rails/editor/props.rb +3 -20
  11. data/lib/ckeditor5/rails/plugins/simple_upload_adapter.rb +1 -1
  12. data/lib/ckeditor5/rails/presets/preset_builder.rb +2 -3
  13. data/lib/ckeditor5/rails/version.rb +1 -1
  14. data/lib/ckeditor5/rails/version_detector.rb +6 -0
  15. data/spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb +104 -0
  16. data/spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb +191 -0
  17. data/spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb +69 -0
  18. data/spec/lib/ckeditor5/rails/cdn/ckeditor_bundle_spec.rb +72 -0
  19. data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +217 -0
  20. data/spec/lib/ckeditor5/rails/context/helpers_spec.rb +67 -0
  21. data/spec/lib/ckeditor5/rails/context/props_spec.rb +70 -0
  22. data/spec/lib/ckeditor5/rails/editor/editable_height_normalizer_spec.rb +50 -0
  23. data/spec/lib/ckeditor5/rails/editor/helpers/config_helpers_spec.rb +52 -0
  24. data/spec/lib/ckeditor5/rails/editor/helpers/editor_helpers_spec.rb +192 -0
  25. data/spec/lib/ckeditor5/rails/editor/props_inline_plugin_spec.rb +43 -0
  26. data/spec/lib/ckeditor5/rails/editor/props_plugin_spec.rb +66 -0
  27. data/spec/lib/ckeditor5/rails/editor/props_spec.rb +104 -0
  28. data/spec/lib/ckeditor5/rails/hooks/form_spec.rb +47 -0
  29. data/spec/lib/ckeditor5/rails/presets/manager_spec.rb +100 -0
  30. data/spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb +98 -0
  31. data/spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb +337 -0
  32. data/spec/lib/ckeditor5/rails/presets/toolbar_builder_spec.rb +70 -0
  33. data/spec/lib/ckeditor5/rails/semver_spec.rb +58 -0
  34. data/spec/lib/ckeditor5/rails/version_detector_spec.rb +131 -0
  35. data/spec/lib/ckeditor5/rails/version_spec.rb +25 -0
  36. data/spec/spec_helper.rb +8 -2
  37. data/spec/support/test_models.rb +6 -0
  38. metadata +44 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5d8c6e89ee351853938c5119d6c8f37385b2762f086e59da587908a8b851b84
4
- data.tar.gz: 11ad938e354484d7e91589abb4172c91e5bb072c5e03308f2b08283f5d8e9844
3
+ metadata.gz: 604312ff3ad324ca5ebbccca70efe7157e9ee67608d6a3b8d3b2d4b7c070213b
4
+ data.tar.gz: 8c44e0810acd1bb837cdcc8a37c4fb3051b5323a243e7b5a16341e9c8e3ef9a7
5
5
  SHA512:
6
- metadata.gz: 72ba9215eb356fe990ce2897d5040df3308a78596d771b6a64a4f84e3d5ce72f13a1aaea87b0ff227ff1c3e920085b208dcc7d65fcbae3e505800dc3803ae40c
7
- data.tar.gz: 0a23dbe70db017e3a401faf1e9613fc0086c5564099f43e98344dba34ac54ca87a38ca92bd763fb4c0a37aa4cf8188539e8ae376fe2ad3a1963cd82d05aef478
6
+ metadata.gz: 54fc1a049c23a9c45bee8fc307de81f7976407850d068f018e715396b93a47e3c37f77c45682ba7ee4f36fe1d5cef156eeb5791dbf0793c35503a93e09e41f8a
7
+ data.tar.gz: 23a2299e6b8685cf120fa40be70d23737ec68f732e6e96af4428d785234600ab17a24596f4d4794093b14fdd0625b20e3a2953950659e0ab0c9deda7fc2c1cc9
data/Gemfile CHANGED
@@ -13,14 +13,9 @@ group :development do
13
13
  gem 'pry-rails', '~> 0.3', '>= 0.3.11'
14
14
  gem 'rails', '~> 7.0', '>= 7.0.0'
15
15
  gem 'rake', '~> 13.2', '>= 13.2.1'
16
- gem 'rspec', '~> 3.13'
17
- gem 'rspec-expectations', '~> 3.13'
18
- gem 'rspec-rails', '~> 7.0'
19
16
  gem 'rubocop', '~> 1.66', require: false
20
17
  gem 'rubocop-rails', '~> 2.26', '>= 2.26.2', require: false
21
18
  gem 'rubocop-rails-omakase', '~> 1.0.0', require: false
22
- gem 'simplecov', '~> 0.21', '>= 0.21.2', require: false
23
- gem 'simplecov_json_formatter', '~> 0.1.4', require: false
24
19
  gem 'simple_form', '~> 5.3', '>= 5.3.0'
25
20
  gem 'slim', '~> 5.2', '>= 5.2.0'
26
21
  gem 'sprockets-rails', '~> 3.2', '>= 3.2.2'
@@ -29,6 +24,12 @@ end
29
24
 
30
25
  group :test, :development do
31
26
  gem 'capybara', '~> 3.40'
27
+ gem 'rspec', '~> 3.13'
28
+ gem 'rspec-expectations', '~> 3.13'
29
+ gem 'rspec-html-matchers', '~> 0.10.0'
30
+ gem 'rspec-rails', '~> 7.0'
31
+ gem 'simplecov', '~> 0.21', '>= 0.21.2', require: false
32
+ gem 'simplecov_json_formatter', '~> 0.1.4', require: false
32
33
  end
33
34
 
34
35
  gemspec
data/README.md CHANGED
@@ -74,6 +74,9 @@ CKEditor5::Rails.configure do
74
74
  # Optionally, you can specify version of CKEditor 5 to use.
75
75
  # If it's not specified the default version specified in the gem will be used.
76
76
  # version '43.3.1'
77
+
78
+ # Upload images to the server using the simple upload adapter, instead of Base64 encoding.
79
+ # simple_upload_adapter
77
80
  end
78
81
  ```
79
82
 
@@ -22,9 +22,9 @@ module CKEditor5::Rails
22
22
  @scripts ||= [
23
23
  Assets::JSExportsMeta.new(
24
24
  create_cdn_url('ckbox', version, 'ckbox.js'),
25
- *translations_js_exports_meta,
26
25
  window_name: 'CKBox'
27
- )
26
+ ),
27
+ *translations_js_exports_meta
28
28
  ]
29
29
  end
30
30
 
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../semver'
4
+ require_relative '../editor/props'
5
+ require_relative '../editor/helpers/config_helpers'
6
+ require_relative '../presets/manager'
7
+ require_relative '../assets/assets_bundle_html_serializer'
8
+
3
9
  require_relative 'url_generator'
4
10
  require_relative 'ckeditor_bundle'
5
11
  require_relative 'ckbox_bundle'
@@ -7,7 +7,7 @@ module CKEditor5::Rails::Context
7
7
  def ckeditor5_context(**config, &block)
8
8
  context_props = Props.new(config)
9
9
 
10
- tag.send(:'ckeditor-context-component', **context_props.to_attributes, &block)
10
+ tag.public_send(:'ckeditor-context-component', **context_props.to_attributes, &block)
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails::Editor
4
+ class InvalidEditableHeightError < ArgumentError; end
5
+
6
+ class EditableHeightNormalizer
7
+ def initialize(editor_type)
8
+ @editor_type = editor_type
9
+ end
10
+
11
+ def normalize(value)
12
+ return nil if value.nil?
13
+
14
+ validate_editor_type!
15
+ convert_to_pixel_value(value)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :editor_type
21
+
22
+ def validate_editor_type!
23
+ return if editor_type == :classic
24
+
25
+ raise InvalidEditableHeightError,
26
+ 'editable_height can be used only with ClassicEditor'
27
+ end
28
+
29
+ def convert_to_pixel_value(value)
30
+ case value
31
+ when Integer then "#{value}px"
32
+ when String then convert_string_to_pixel_value(value)
33
+ else
34
+ raise_invalid_height_error(value)
35
+ end
36
+ end
37
+
38
+ def convert_string_to_pixel_value(value)
39
+ return value if value.match?(/^\d+px$/)
40
+
41
+ raise_invalid_height_error(value)
42
+ end
43
+
44
+ def raise_invalid_height_error(value)
45
+ raise InvalidEditableHeightError,
46
+ "editable_height must be an integer representing pixels or string ending with 'px'\n" \
47
+ "(e.g. 500 or '500px'). Got: #{value.inspect}"
48
+ end
49
+ end
50
+ end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CKEditor5::Rails
4
- module Editor::Helpers::Config
3
+ module CKEditor5::Rails::Editor::Helpers
4
+ module Config
5
5
  def ckeditor5_element_ref(selector)
6
6
  { '$element': selector }
7
7
  end
8
8
 
9
9
  def ckeditor5_preset(name = nil, &block)
10
- return Engine.find_preset(name) if name
10
+ return CKEditor5::Rails::Engine.find_preset(name) if name
11
11
 
12
12
  raise ArgumentError, 'Configuration block is required for preset definition' unless block_given?
13
13
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../version_detector'
4
+ require_relative '../../presets/preset_builder'
3
5
  require_relative 'config_helpers'
4
6
 
5
7
  module CKEditor5::Rails
@@ -8,7 +10,6 @@ module CKEditor5::Rails
8
10
 
9
11
  class EditorContextError < StandardError; end
10
12
  class PresetNotFoundError < ArgumentError; end
11
- class InvalidEditableHeightError < ArgumentError; end
12
13
 
13
14
  # Creates a CKEditor 5 editor instance
14
15
  #
@@ -45,15 +46,15 @@ module CKEditor5::Rails
45
46
 
46
47
  tag_attributes = html_attributes.merge(editor_props.to_attributes)
47
48
 
48
- tag.send(:'ckeditor-component', **tag_attributes, &block)
49
+ tag.public_send(:'ckeditor-component', **tag_attributes, &block)
49
50
  end
50
51
 
51
52
  def ckeditor5_editable(name = nil, **kwargs, &block)
52
- tag.send(:'ckeditor-editable-component', name: name, **kwargs, &block)
53
+ tag.public_send(:'ckeditor-editable-component', name: name, **kwargs, &block)
53
54
  end
54
55
 
55
56
  def ckeditor5_ui_part(name, **kwargs, &block)
56
- tag.send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
57
+ tag.public_send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
57
58
  end
58
59
 
59
60
  def ckeditor5_toolbar(**kwargs)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'props_plugin'
4
+ require_relative 'editable_height_normalizer'
4
5
 
5
6
  module CKEditor5::Rails::Editor
6
7
  class Props
@@ -19,7 +20,7 @@ module CKEditor5::Rails::Editor
19
20
  @watchdog = watchdog
20
21
  @type = type
21
22
  @config = config
22
- @editable_height = normalize_editable_height(editable_height)
23
+ @editable_height = EditableHeightNormalizer.new(type).normalize(editable_height)
23
24
  end
24
25
 
25
26
  def to_attributes
@@ -48,7 +49,7 @@ module CKEditor5::Rails::Editor
48
49
  end
49
50
 
50
51
  def serialize_translations
51
- controller_context[:bundle].translations_scripts.map(&:to_h).to_json
52
+ controller_context[:bundle]&.translations_scripts&.map(&:to_h).to_json || '[]'
52
53
  end
53
54
 
54
55
  def serialize_plugins
@@ -61,23 +62,5 @@ module CKEditor5::Rails::Editor
61
62
  .tap { |cfg| cfg[:licenseKey] = controller_context[:license_key] if controller_context[:license_key] }
62
63
  .to_json
63
64
  end
64
-
65
- def normalize_editable_height(editable_height)
66
- return nil if editable_height.nil?
67
-
68
- unless type == :classic
69
- raise InvalidEditableHeightError,
70
- 'editable_height can be used only with ClassicEditor'
71
- end
72
-
73
- case editable_height
74
- when String, /^\d+px$/ then editable_height
75
- when Integer, /^\d+$/ then "#{editable_height}px"
76
- else
77
- raise InvalidEditableHeightError,
78
- "editable_height must be an integer representing pixels or string ending with 'px'\n" \
79
- "(e.g. 500 or '500px'). Got: #{editable_height.inspect}"
80
- end
81
- end
82
65
  end
83
66
  end
@@ -81,7 +81,7 @@ module CKEditor5::Rails::Plugins
81
81
  JAVASCRIPT
82
82
 
83
83
  def initialize
84
- super(:SimpleUpload, PLUGIN_CODE)
84
+ super(:SimpleUploadAdapter, PLUGIN_CODE)
85
85
  end
86
86
  end
87
87
  end
@@ -104,7 +104,7 @@ module CKEditor5::Rails
104
104
  end
105
105
 
106
106
  def version(version = nil)
107
- return @version.to_s if version.nil?
107
+ return @version&.to_s if version.nil?
108
108
 
109
109
  if @automatic_upgrades && version
110
110
  detected = VersionDetector.latest_safe_version(version)
@@ -164,7 +164,7 @@ module CKEditor5::Rails
164
164
 
165
165
  return unless block
166
166
 
167
- builder = ArrayBuilder.new(@config[:toolbar][:items])
167
+ builder = ToolbarBuilder.new(@config[:toolbar][:items])
168
168
  builder.instance_eval(&block)
169
169
  end
170
170
 
@@ -174,7 +174,6 @@ module CKEditor5::Rails
174
174
 
175
175
  def plugin(name, **kwargs)
176
176
  plugin_obj = PluginsBuilder.create_plugin(name, **kwargs)
177
-
178
177
  @config[:plugins] << plugin_obj
179
178
  plugin_obj
180
179
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.15.7'
5
+ VERSION = '1.15.9'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '43.3.1'
8
8
  end
@@ -4,6 +4,8 @@ require 'net/http'
4
4
  require 'json'
5
5
  require 'singleton'
6
6
  require 'monitor'
7
+
8
+ require_relative 'version'
7
9
  require_relative 'semver'
8
10
 
9
11
  module CKEditor5::Rails
@@ -22,6 +24,10 @@ module CKEditor5::Rails
22
24
  @monitor = Monitor.new
23
25
  end
24
26
 
27
+ def clear_cache!
28
+ @monitor.synchronize { @cache.clear }
29
+ end
30
+
25
31
  def latest_safe_version(current_version)
26
32
  @monitor.synchronize do
27
33
  cache_key = "#{current_version}_latest"
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
6
+ let(:test_bundle_class) do
7
+ Class.new(CKEditor5::Rails::Assets::AssetsBundle) do
8
+ attr_accessor :scripts, :stylesheets
9
+
10
+ def initialize(scripts, stylesheets)
11
+ @scripts = scripts
12
+ @stylesheets = stylesheets
13
+ super()
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:bundle) { test_bundle_class.new(scripts, stylesheets) }
19
+
20
+ let(:scripts) do
21
+ [
22
+ CKEditor5::Rails::Assets::JSExportsMeta.new(
23
+ 'https://cdn.com/script1.js',
24
+ window_name: 'CKEditor5'
25
+ ),
26
+ CKEditor5::Rails::Assets::JSExportsMeta.new(
27
+ 'https://cdn.com/script2.js',
28
+ import_name: '@ckeditor/script2'
29
+ )
30
+ ]
31
+ end
32
+
33
+ let(:stylesheets) { ['https://cdn.com/style1.css', 'https://cdn.com/style2.css'] }
34
+ let(:preloads) { bundle.preloads }
35
+
36
+ subject(:serializer) { described_class.new(bundle) }
37
+
38
+ describe '#initialize' do
39
+ context 'with invalid bundle' do
40
+ let(:bundle) { 'not a bundle' }
41
+
42
+ it 'raises TypeError' do
43
+ expect { serializer }.to raise_error(TypeError, 'bundle must be an instance of AssetsBundle')
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#to_html' do
49
+ subject(:html) { serializer.to_html }
50
+
51
+ it 'includes window scripts' do
52
+ expect(html).to include(
53
+ '<script src="https://cdn.com/script1.js" nonce="true" crossorigin="anonymous">'
54
+ )
55
+ end
56
+
57
+ it 'includes import map' do
58
+ expect(html).to include('type="importmap"')
59
+ expect(html).to include('"@ckeditor/script2":"https://cdn.com/script2.js"')
60
+ end
61
+
62
+ it 'includes stylesheet links' do
63
+ stylesheets.each do |url|
64
+ expect(html).to include("<link href=\"#{url}\" rel=\"stylesheet\" crossorigin=\"anonymous\">")
65
+ end
66
+ end
67
+
68
+ it 'includes preload links' do
69
+ expect(html).to include(
70
+ '<link href="https://cdn.com/style1.css" rel="preload" as="style" crossorigin="anonymous">'
71
+ )
72
+
73
+ expect(html).to include(
74
+ '<link href="https://cdn.com/style2.css" rel="preload" as="style" crossorigin="anonymous">'
75
+ )
76
+
77
+ expect(html).to include(
78
+ '<link href="https://cdn.com/script1.js" rel="preload" as="script" crossorigin="anonymous">'
79
+ )
80
+
81
+ expect(html).to include(
82
+ '<link href="https://cdn.com/script2.js" rel="preload" as="script" crossorigin="anonymous">'
83
+ )
84
+ end
85
+
86
+ it 'includes web component script' do
87
+ expect(html).to include('<script type="module" nonce="true">')
88
+ end
89
+ end
90
+
91
+ describe '.url_resource_preload_type' do
92
+ it 'returns correct type for js files' do
93
+ expect(described_class.url_resource_preload_type('file.js')).to eq('script')
94
+ end
95
+
96
+ it 'returns correct type for css files' do
97
+ expect(described_class.url_resource_preload_type('file.css')).to eq('style')
98
+ end
99
+
100
+ it 'returns fetch for unknown extensions' do
101
+ expect(described_class.url_resource_preload_type('file.unknown')).to eq('fetch')
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Assets::AssetsBundle do
6
+ let(:concrete_class) do
7
+ Class.new(described_class) do
8
+ def scripts
9
+ []
10
+ end
11
+
12
+ def stylesheets
13
+ []
14
+ end
15
+ end
16
+ end
17
+
18
+ describe '#initialize' do
19
+ it 'raises error when required methods are not implemented' do
20
+ expect { described_class.new }.to raise_error(NotImplementedError)
21
+ end
22
+
23
+ it 'initializes successfully when required methods are implemented' do
24
+ expect { concrete_class.new }.not_to raise_error
25
+ end
26
+ end
27
+
28
+ describe '#empty?' do
29
+ subject(:bundle) { concrete_class.new }
30
+
31
+ it 'returns true when no assets are present' do
32
+ expect(bundle).to be_empty
33
+ end
34
+ end
35
+
36
+ describe '#translations_scripts' do
37
+ let(:bundle) { concrete_class.new }
38
+ let(:translation_script) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta, translation?: true) }
39
+ let(:regular_script) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta, translation?: false) }
40
+
41
+ before do
42
+ allow(bundle).to receive(:scripts).and_return([translation_script, regular_script])
43
+ end
44
+
45
+ it 'returns only translation scripts' do
46
+ expect(bundle.translations_scripts).to eq([translation_script])
47
+ end
48
+ end
49
+
50
+ describe '#<<' do
51
+ let(:script1) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta) }
52
+ let(:script2) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta) }
53
+ let(:stylesheet1) { '/path/to/style1.css' }
54
+ let(:stylesheet2) { '/path/to/style2.css' }
55
+
56
+ let(:bundle1) do
57
+ Class.new(described_class) do
58
+ attr_writer :scripts, :stylesheets
59
+
60
+ def scripts
61
+ @scripts ||= []
62
+ end
63
+
64
+ def stylesheets
65
+ @stylesheets ||= []
66
+ end
67
+ end.new
68
+ end
69
+
70
+ let(:bundle2) do
71
+ Class.new(described_class) do
72
+ attr_writer :scripts, :stylesheets
73
+
74
+ def scripts
75
+ @scripts ||= []
76
+ end
77
+
78
+ def stylesheets
79
+ @stylesheets ||= []
80
+ end
81
+ end.new
82
+ end
83
+
84
+ before do
85
+ bundle1.scripts = [script1]
86
+ bundle1.stylesheets = [stylesheet1]
87
+ bundle2.scripts = [script2]
88
+ bundle2.stylesheets = [stylesheet2]
89
+ end
90
+
91
+ it 'raises TypeError when argument is not an AssetsBundle' do
92
+ expect { bundle1 << 'not a bundle' }.to raise_error(TypeError)
93
+ end
94
+
95
+ it 'merges scripts and stylesheets from both bundles' do
96
+ bundle1 << bundle2
97
+
98
+ expect(bundle1.scripts).to eq([script1, script2])
99
+ expect(bundle1.stylesheets).to eq([stylesheet1, stylesheet2])
100
+ end
101
+ end
102
+
103
+ describe '#preloads' do
104
+ let(:script1) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta, url: '/js/script1.js') }
105
+ let(:script2) { instance_double(CKEditor5::Rails::Assets::JSExportsMeta, url: '/js/script2.js') }
106
+ let(:stylesheet1) { '/css/style1.css' }
107
+ let(:stylesheet2) { '/css/style2.css' }
108
+
109
+ let(:bundle) do
110
+ Class.new(described_class) do
111
+ attr_writer :scripts, :stylesheets
112
+
113
+ def scripts
114
+ @scripts ||= []
115
+ end
116
+
117
+ def stylesheets
118
+ @stylesheets ||= []
119
+ end
120
+ end.new
121
+ end
122
+
123
+ before do
124
+ bundle.scripts = [script1, script2]
125
+ bundle.stylesheets = [stylesheet1, stylesheet2]
126
+ end
127
+
128
+ it 'returns array of stylesheet paths and script urls' do
129
+ expect(bundle.preloads).to eq([
130
+ '/css/style1.css',
131
+ '/css/style2.css',
132
+ '/js/script1.js',
133
+ '/js/script2.js'
134
+ ])
135
+ end
136
+ end
137
+ end
138
+
139
+ RSpec.describe CKEditor5::Rails::Assets::JSExportsMeta do
140
+ let(:url) { '/path/to/script.js' }
141
+
142
+ describe '#initialize' do
143
+ it 'creates instance with import_name' do
144
+ meta = described_class.new(url, import_name: 'module')
145
+ expect(meta.import_name).to eq('module')
146
+ end
147
+
148
+ it 'creates instance with window_name' do
149
+ meta = described_class.new(url, window_name: 'MyModule')
150
+ expect(meta.window_name).to eq('MyModule')
151
+ end
152
+
153
+ it 'marks as translation when specified' do
154
+ meta = described_class.new(url, translation: true, import_name: 'module')
155
+ expect(meta).to be_translation
156
+ end
157
+ end
158
+ end
159
+
160
+ RSpec.describe CKEditor5::Rails::Assets::JSImportMeta do
161
+ describe '#initialize' do
162
+ it 'raises error when neither import_name nor window_name is provided' do
163
+ expect { described_class.new }.to raise_error(ArgumentError)
164
+ end
165
+
166
+ it 'raises error when import_as is present without import_name' do
167
+ expect { described_class.new(import_as: 'alias', window_name: 'Module') }
168
+ .to raise_error(ArgumentError)
169
+ end
170
+
171
+ it 'creates valid instance with import_name' do
172
+ meta = described_class.new(import_name: 'module')
173
+ expect(meta).to be_esm
174
+ end
175
+
176
+ it 'creates valid instance with window_name' do
177
+ meta = described_class.new(window_name: 'Module')
178
+ expect(meta).to be_window
179
+ end
180
+ end
181
+
182
+ describe '#to_h' do
183
+ it 'returns hash with only present values' do
184
+ meta = described_class.new(import_name: 'module', import_as: 'alias')
185
+ expect(meta.to_h).to eq({
186
+ import_name: 'module',
187
+ import_as: 'alias'
188
+ })
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Cdn::CKBoxBundle do
6
+ let(:version) { CKEditor5::Rails::Semver.new('2.6.0') }
7
+ let(:cdn) { :jsdelivr }
8
+ let(:theme) { :lark }
9
+ let(:translations) { [] }
10
+ let(:bundle) { described_class.new(version, theme: theme, cdn: cdn, translations: translations) }
11
+
12
+ describe '#initialize' do
13
+ context 'with valid parameters' do
14
+ it 'creates bundle successfully' do
15
+ expect { bundle }.not_to raise_error
16
+ end
17
+ end
18
+
19
+ context 'with invalid parameters' do
20
+ it 'raises error for invalid version' do
21
+ expect { described_class.new('invalid', theme: theme) }
22
+ .to raise_error(ArgumentError, 'version must be semver')
23
+ end
24
+
25
+ it 'raises error for invalid theme type' do
26
+ expect { described_class.new(version, theme: 123) }
27
+ .to raise_error(ArgumentError, 'theme must be a string or symbol')
28
+ end
29
+
30
+ it 'raises error for invalid translations type' do
31
+ expect { described_class.new(version, theme: theme, translations: 'invalid') }
32
+ .to raise_error(ArgumentError, 'translations must be an array')
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#scripts' do
38
+ it 'returns array with main script' do
39
+ expect(bundle.scripts.first).to be_a(CKEditor5::Rails::Assets::JSExportsMeta)
40
+ expect(bundle.scripts.first.url).to include('ckbox.js')
41
+ expect(bundle.scripts.first.window_name).to eq('CKBox')
42
+ end
43
+
44
+ context 'with translations' do
45
+ let(:translations) { [:pl] }
46
+
47
+ it 'includes translation scripts' do
48
+ translation_script = bundle.scripts.last
49
+ expect(translation_script.url).to include('translations/pl.js')
50
+ expect(translation_script.window_name).to eq('CKBOX_TRANSLATIONS')
51
+ expect(translation_script.translation?).to be true
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#stylesheets' do
57
+ it 'returns array with theme stylesheet' do
58
+ expect(bundle.stylesheets.first).to include('styles/themes/lark.css')
59
+ end
60
+
61
+ context 'with custom theme' do
62
+ let(:theme) { :custom }
63
+
64
+ it 'uses custom theme in stylesheet path' do
65
+ expect(bundle.stylesheets.first).to include('styles/themes/custom.css')
66
+ end
67
+ end
68
+ end
69
+ end