ckeditor5 1.15.9 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/lib/ckeditor5/rails/assets/webcomponents/components/context.mjs +23 -0
  4. data/lib/ckeditor5/rails/assets/webcomponents/components/editor.mjs +24 -0
  5. data/lib/ckeditor5/rails/assets/webcomponents/utils.mjs +33 -10
  6. data/lib/ckeditor5/rails/concerns/checksum.rb +15 -0
  7. data/lib/ckeditor5/rails/context/props.rb +17 -2
  8. data/lib/ckeditor5/rails/editor/props.rb +16 -3
  9. data/lib/ckeditor5/rails/editor/props_base_plugin.rb +19 -0
  10. data/lib/ckeditor5/rails/editor/props_inline_plugin.rb +6 -3
  11. data/lib/ckeditor5/rails/editor/props_plugin.rb +6 -4
  12. data/lib/ckeditor5/rails/engine.rb +3 -2
  13. data/lib/ckeditor5/rails/presets/plugins_builder.rb +9 -9
  14. data/lib/ckeditor5/rails/presets/preset_builder.rb +4 -6
  15. data/lib/ckeditor5/rails/version.rb +1 -1
  16. data/lib/ckeditor5/rails.rb +1 -0
  17. data/spec/e2e/features/editor_types_spec.rb +178 -0
  18. data/spec/e2e/features/form_integration_spec.rb +60 -0
  19. data/spec/e2e/spec_helper.rb +43 -0
  20. data/spec/e2e/support/eventually.rb +14 -0
  21. data/spec/e2e/support/form_helpers.rb +37 -0
  22. data/spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb +7 -0
  23. data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +19 -1
  24. data/spec/lib/ckeditor5/rails/concerns/checksum_spec.rb +50 -0
  25. data/spec/lib/ckeditor5/rails/context/props_spec.rb +7 -1
  26. data/spec/lib/ckeditor5/rails/editor/props_base_plugin_spec.rb +27 -0
  27. data/spec/lib/ckeditor5/rails/editor/props_spec.rb +2 -0
  28. data/spec/lib/ckeditor5/rails/engine_spec.rb +88 -0
  29. data/spec/lib/ckeditor5/rails/hooks/form_spec.rb +156 -0
  30. data/spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb +100 -0
  31. data/spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb +10 -10
  32. data/spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb +10 -0
  33. data/spec/spec_helper.rb +2 -2
  34. metadata +22 -2
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'e2e/spec_helper'
4
+
5
+ RSpec.describe 'Form Integration', type: :feature, js: true do
6
+ before do
7
+ visit('form')
8
+ setup_form_tracking(page)
9
+ end
10
+
11
+ shared_examples 'a form with CKEditor' do |form_testid, editor_testid, submit_testid| # rubocop:disable Metrics/BlockLength
12
+ let(:form) { find("[data-testid='#{form_testid}']") }
13
+ let(:editor) { find("[data-testid='#{editor_testid}']") }
14
+ let(:editable) { editor.find('.ck-editor__editable') }
15
+ let(:text_field) { editor.find('textarea', visible: :hidden) }
16
+ let(:submit_button) { find("[data-testid='#{submit_testid}']") }
17
+
18
+ it 'loads editor properly' do
19
+ expect(page).to have_css("[data-testid='#{editor_testid}'] .ck-editor__editable")
20
+ expect(editor).to have_invisible_textarea
21
+ end
22
+
23
+ it 'validates required fields' do
24
+ editable.click
25
+ editable.send_keys([[:control, 'a'], :backspace])
26
+
27
+ text_field.set('')
28
+ submit_button.click
29
+
30
+ expect(form).not_to have_been_submitted
31
+ expect(text_field).to be_invalid
32
+ end
33
+
34
+ it 'submits with valid data' do
35
+ editable.click
36
+ editable.send_keys('New content')
37
+ text_field.set('Second field value')
38
+
39
+ submit_button.click
40
+
41
+ eventually do
42
+ expect(form).to have_been_submitted
43
+ end
44
+ end
45
+ end
46
+
47
+ describe 'Rails form' do
48
+ it_behaves_like 'a form with CKEditor',
49
+ 'rails-form',
50
+ 'rails-form-editor',
51
+ 'rails-form-submit'
52
+ end
53
+
54
+ describe 'Simple form' do
55
+ it_behaves_like 'a form with CKEditor',
56
+ 'simple-form',
57
+ 'simple-form-editor',
58
+ 'simple-form-submit'
59
+ end
60
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara'
4
+ require 'capybara/rspec'
5
+ require 'capybara/cuprite'
6
+
7
+ ENV['RAILS_ENV'] ||= 'test'
8
+
9
+ require File.expand_path('../../sandbox/config/environment', __dir__)
10
+
11
+ require 'capybara/rails'
12
+
13
+ Capybara.app = Rails.application
14
+
15
+ Capybara.register_driver(:cuprite) do |app|
16
+ driver = Capybara::Cuprite::Driver.new(
17
+ app,
18
+ window_size: [1200, 800],
19
+ headless: ENV['HEADLESS'] == 'true',
20
+ browser_options: {
21
+ 'no-sandbox': nil,
22
+ 'disable-gpu': nil,
23
+ 'enable-logging': nil
24
+ },
25
+ process_timeout: 20,
26
+ timeout: 20,
27
+ inspector: true
28
+ )
29
+
30
+ process = driver.browser.process
31
+ puts ''
32
+ puts "Browser: #{process.browser_version}"
33
+ puts "Protocol: #{process.protocol_version}"
34
+ puts "V8: #{process.v8_version}"
35
+ puts "Webkit: #{process.webkit_version}"
36
+ driver
37
+ end
38
+
39
+ Capybara.server = :webrick
40
+ Capybara.default_driver = :cuprite
41
+ Capybara.javascript_driver = :cuprite
42
+
43
+ Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add eventually helper for async operations
4
+ def eventually(timeout: 5, delay: 0.1)
5
+ deadline = Time.zone.now + timeout
6
+ loop do
7
+ yield
8
+ break
9
+ rescue RSpec::Expectations::ExpectationNotMetError, StandardError => e
10
+ raise e if Time.zone.now >= deadline
11
+
12
+ sleep delay
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FormHelpers
4
+ def setup_form_tracking(driver)
5
+ driver.execute_script <<~JS
6
+ window.lastSubmittedForm = null;
7
+
8
+ document.addEventListener('submit', (e) => {
9
+ e.preventDefault();
10
+ window.lastSubmittedForm = e.target.id;
11
+ });
12
+ JS
13
+ end
14
+ end
15
+
16
+ RSpec.configure do |config|
17
+ config.include FormHelpers, type: :feature
18
+ end
19
+
20
+ RSpec::Matchers.define :be_invalid do
21
+ match do |element|
22
+ element[:validity] == 'false' ||
23
+ element.evaluate_script('!this.validity.valid')
24
+ end
25
+ end
26
+
27
+ RSpec::Matchers.define :have_been_submitted do
28
+ match do |form|
29
+ page.evaluate_script('window.lastSubmittedForm') == form['id']
30
+ end
31
+ end
32
+
33
+ RSpec::Matchers.define :have_invisible_textarea do
34
+ match do |element|
35
+ element.has_css?('textarea', visible: :hidden)
36
+ end
37
+ end
@@ -86,6 +86,13 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
86
86
  it 'includes web component script' do
87
87
  expect(html).to include('<script type="module" nonce="true">')
88
88
  end
89
+
90
+ it 'memoizes scripts import map' do
91
+ first_call = serializer.send(:scripts_import_map_tag)
92
+ second_call = serializer.send(:scripts_import_map_tag)
93
+
94
+ expect(first_call.object_id).to eq(second_call.object_id)
95
+ end
89
96
  end
90
97
 
91
98
  describe '.url_resource_preload_type' do
@@ -160,7 +160,25 @@ RSpec.describe CKEditor5::Rails::Cdn::Helpers do
160
160
  )
161
161
  end
162
162
 
163
- it 'raises error when version is missing' do
163
+ before do
164
+ allow(helper).to receive(:merge_with_editor_preset).and_return({})
165
+ end
166
+
167
+ it 'raises error about missing required parameters' do
168
+ expect { helper.ckeditor5_assets(preset: :default) }
169
+ .to raise_error(NoMatchingPatternKeyError)
170
+ end
171
+ end
172
+
173
+ context 'with empty hash from preset' do
174
+ let(:preset) do
175
+ instance_double(
176
+ CKEditor5::Rails::Presets::PresetBuilder,
177
+ to_h_with_overrides: {}
178
+ )
179
+ end
180
+
181
+ it 'raises error about missing version and type' do
164
182
  expect { helper.ckeditor5_assets(preset: :default) }
165
183
  .to raise_error(ArgumentError, /forgot to define version/)
166
184
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Concerns::Checksum do
6
+ let(:dummy_class) do
7
+ Class.new do
8
+ include CKEditor5::Rails::Concerns::Checksum
9
+
10
+ public :calculate_object_checksum
11
+ end
12
+ end
13
+
14
+ subject(:instance) { dummy_class.new }
15
+
16
+ describe '#calculate_object_checksum' do
17
+ it 'returns a 16-character string' do
18
+ result = instance.calculate_object_checksum({ test: 'value' })
19
+ expect(result).to eq(
20
+ 'f98be16ebfa861cb39a61faff9e52b33f5bcc16bb6ae72e728d226dc07093932'
21
+ )
22
+ end
23
+
24
+ it 'returns consistent checksums for the same input' do
25
+ input = { name: 'test', value: 123 }
26
+ first_result = instance.calculate_object_checksum(input)
27
+ second_result = instance.calculate_object_checksum(input)
28
+ expect(first_result).to eq(second_result)
29
+ end
30
+
31
+ it 'returns different checksums for different inputs' do
32
+ result1 = instance.calculate_object_checksum({ a: 1 })
33
+ result2 = instance.calculate_object_checksum({ a: 2 })
34
+ expect(result1).not_to eq(result2)
35
+ end
36
+
37
+ it 'handles arrays' do
38
+ result = instance.calculate_object_checksum([1, 2, 3])
39
+ expect(result).to eq(
40
+ 'a615eeaee21de5179de080de8c3052c8da901138406ba71c38c032845f7d54f4'
41
+ )
42
+ end
43
+
44
+ it 'is order dependent for hashes' do
45
+ result1 = instance.calculate_object_checksum({ a: 1, b: 2 })
46
+ result2 = instance.calculate_object_checksum({ b: 2, a: 1 })
47
+ expect(result1).not_to eq(result2)
48
+ end
49
+ end
50
+ end
@@ -25,9 +25,15 @@ RSpec.describe CKEditor5::Rails::Context::Props do
25
25
  describe '#to_attributes' do
26
26
  subject(:attributes) { props.to_attributes }
27
27
 
28
+ it 'returns integrity property' do
29
+ expect(attributes[:integrity]).to eq(
30
+ '24e46c3ee19f6764930b38ecdf62c0ac824a0acbe6616b46199d892afb211acb'
31
+ )
32
+ end
33
+
28
34
  it 'returns a hash with plugins and config keys' do
29
35
  expect(attributes).to be_a(Hash)
30
- expect(attributes.keys).to match_array(%i[plugins config])
36
+ expect(attributes.keys).to match_array(%i[plugins integrity config])
31
37
  end
32
38
 
33
39
  describe ':plugins key' do
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Editor::PropsBasePlugin do
6
+ let(:concrete_class) do
7
+ Class.new(described_class) do
8
+ def to_unsafe_h
9
+ { type: :test, name: name }
10
+ end
11
+ end
12
+ end
13
+
14
+ let(:instance) { concrete_class.new(:TestPlugin) }
15
+
16
+ describe '#initialize' do
17
+ it 'sets the name attribute' do
18
+ expect(instance.name).to eq(:TestPlugin)
19
+ end
20
+ end
21
+
22
+ describe '#to_h' do
23
+ it 'raises NotImplementedError' do
24
+ expect { instance.to_h }.to raise_error(NotImplementedError)
25
+ end
26
+ end
27
+ end
@@ -28,11 +28,13 @@ RSpec.describe CKEditor5::Rails::Editor::Props do
28
28
 
29
29
  it 'includes required attributes' do
30
30
  attributes = props.to_attributes
31
+
31
32
  expect(attributes).to include(
32
33
  type: 'ClassicEditor',
33
34
  translations: String,
34
35
  plugins: String,
35
36
  config: String,
37
+ integrity: '358d88b83d041f208d94ac957b2fd68135f1caab5c0d101d33cf04d5d39d81ef',
36
38
  watchdog: true
37
39
  )
38
40
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Engine do
6
+ describe 'configuration' do
7
+ let(:preset) { instance_double(CKEditor5::Rails::Presets::PresetBuilder) }
8
+ let(:preset_manager) { instance_double(CKEditor5::Rails::Presets::Manager) }
9
+
10
+ before do
11
+ allow(described_class).to receive(:base).and_return(ActiveSupport::OrderedOptions.new)
12
+ allow(described_class.base).to receive(:presets).and_return(preset_manager)
13
+ allow(preset_manager).to receive(:default).and_return(preset)
14
+ end
15
+
16
+ it 'has default configuration' do
17
+ expect(described_class.base).to be_a(ActiveSupport::OrderedOptions)
18
+
19
+ default_preset = described_class.default_preset
20
+
21
+ expect(default_preset.type).to eq(:classic)
22
+ expect(default_preset.toolbar.items).to include(:undo, :redo, :'|', :heading)
23
+ expect(default_preset.plugins.items.map(&:name)).to include(:Essentials, :Paragraph, :Heading)
24
+ end
25
+
26
+ describe '.configure' do
27
+ it 'yields configuration proxy' do
28
+ yielded_config = nil
29
+ described_class.configure do |config|
30
+ yielded_config = config
31
+ end
32
+ expect(yielded_config).to be_a(described_class::ConfigurationProxy)
33
+ end
34
+
35
+ it 'allows configuring default preset' do
36
+ described_class.configure do
37
+ automatic_upgrades enabled: false
38
+ version '35.0.0'
39
+ license_key '1234567'
40
+ end
41
+
42
+ expect(described_class.default_preset.version).to eq('35.0.0')
43
+ expect(described_class.default_preset.license_key).to eq('1234567')
44
+ end
45
+ end
46
+
47
+ describe '.find_preset' do
48
+ before do
49
+ allow(preset_manager).to receive(:[]).with(:custom).and_return(preset)
50
+ allow(preset_manager).to receive(:[]).with(
51
+ kind_of(CKEditor5::Rails::Presets::PresetBuilder)
52
+ ).and_return(preset)
53
+ end
54
+
55
+ it 'returns preset instance if provided' do
56
+ test_preset = CKEditor5::Rails::Presets::PresetBuilder.new
57
+ expect(described_class.find_preset(test_preset)).to eq(test_preset)
58
+ end
59
+
60
+ it 'looks up preset by name' do
61
+ expect(described_class.find_preset(:custom)).to eq(preset)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe 'initializers' do
67
+ describe 'helper initializer' do
68
+ it 'includes helpers in ActionView and ActionController' do
69
+ expect(ActionView::Base.included_modules).to include(CKEditor5::Rails::Helpers)
70
+ expect(ActionController::Base.included_modules).to include(CKEditor5::Rails::Helpers)
71
+ end
72
+ end
73
+
74
+ describe 'form_builder initializer' do
75
+ it 'includes FormBuilderExtension in ActionView::Helpers::FormBuilder' do
76
+ expect(ActionView::Helpers::FormBuilder.included_modules)
77
+ .to include(CKEditor5::Rails::Hooks::Form::FormBuilderExtension)
78
+ end
79
+ end
80
+
81
+ describe 'simple_form initializer', if: defined?(SimpleForm) do
82
+ it 'registers ckeditor5 input type' do
83
+ expect(SimpleForm::FormBuilder.mappings[:ckeditor5])
84
+ .to eq(CKEditor5::Rails::Hooks::SimpleForm::CKEditor5Input)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -42,6 +42,162 @@ RSpec.describe CKEditor5::Rails::Hooks::Form do
42
42
  })
43
43
  end
44
44
  end
45
+
46
+ context 'with as parameter' do
47
+ subject(:rendered_editor) do
48
+ builder.build_editor(:content, as: 'custom_field')
49
+ end
50
+
51
+ it 'uses custom field name' do
52
+ expect(rendered_editor).to have_tag('ckeditor-component', with: {
53
+ name: 'post[custom_field]'
54
+ })
55
+ end
56
+ end
57
+
58
+ context 'without object_name' do
59
+ let(:builder) { described_class.new('', post, template) }
60
+
61
+ it 'uses method name as field name' do
62
+ expect(rendered_editor).to have_tag('ckeditor-component', with: {
63
+ name: 'content'
64
+ })
65
+ end
66
+
67
+ context 'with as parameter' do
68
+ subject(:rendered_editor) do
69
+ builder.build_editor(:content, as: 'custom_field')
70
+ end
71
+
72
+ it 'uses as parameter as field name' do
73
+ expect(rendered_editor).to have_tag('ckeditor-component', with: {
74
+ name: 'custom_field'
75
+ })
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'with initial data handling' do
81
+ before do
82
+ allow(template).to receive(:ckeditor5_editor)
83
+ end
84
+
85
+ context 'when object responds to the method' do
86
+ it 'passes object method value as initial_data' do
87
+ builder.build_editor(:content)
88
+
89
+ expect(template).to have_received(:ckeditor5_editor)
90
+ .with(hash_including(initial_data: 'Initial content'))
91
+ end
92
+ end
93
+
94
+ context 'when object does not respond to the method' do
95
+ let(:post) { double('Post') }
96
+
97
+ it 'passes options initial_data value' do
98
+ builder.build_editor(:content, initial_data: 'Provided content')
99
+
100
+ expect(template).to have_received(:ckeditor5_editor)
101
+ .with(hash_including(initial_data: 'Provided content'))
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'with validation classes handling' do
107
+ before do
108
+ allow(template).to receive(:ckeditor5_editor)
109
+ end
110
+
111
+ context 'when object has errors on the field' do
112
+ let(:post) do
113
+ instance_double('Post', errors: { content: ['is invalid'] })
114
+ end
115
+
116
+ it 'adds is-invalid class' do
117
+ builder.build_editor(:content, class: 'custom-class')
118
+
119
+ expect(template).to have_received(:ckeditor5_editor)
120
+ .with(hash_including(class: 'custom-class is-invalid'))
121
+ end
122
+
123
+ it 'adds is-invalid class when no initial class exists' do
124
+ builder.build_editor(:content)
125
+
126
+ expect(template).to have_received(:ckeditor5_editor)
127
+ .with(hash_including(class: 'is-invalid'))
128
+ end
129
+ end
130
+
131
+ context 'when object has no errors' do
132
+ let(:post) do
133
+ instance_double('Post', errors: {})
134
+ end
135
+
136
+ it 'keeps original class unchanged' do
137
+ builder.build_editor(:content, class: 'custom-class')
138
+
139
+ expect(template).to have_received(:ckeditor5_editor)
140
+ .with(hash_including(class: 'custom-class'))
141
+ end
142
+ end
143
+
144
+ context 'when object does not respond to errors' do
145
+ let(:post) { double('Post') }
146
+
147
+ it 'keeps original class unchanged' do
148
+ builder.build_editor(:content, class: 'custom-class')
149
+
150
+ expect(template).to have_received(:ckeditor5_editor)
151
+ .with(hash_including(class: 'custom-class'))
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ describe CKEditor5::Rails::Hooks::Form::FormBuilderExtension do
159
+ let(:template) { instance_double('ActionView::Base') }
160
+ let(:object) { double('Post') }
161
+ let(:builder) do
162
+ Class.new do
163
+ include CKEditor5::Rails::Hooks::Form::FormBuilderExtension
164
+ attr_reader :object_name, :object, :template
165
+
166
+ def initialize(object_name, object, template)
167
+ @object_name = object_name
168
+ @object = object
169
+ @template = template
170
+ end
171
+ end.new('post', object, template)
172
+ end
173
+
174
+ describe '#ckeditor5' do
175
+ let(:input_builder) { instance_double(CKEditor5::Rails::Hooks::Form::EditorInputBuilder) }
176
+
177
+ before do
178
+ allow(CKEditor5::Rails::Hooks::Form::EditorInputBuilder)
179
+ .to receive(:new)
180
+ .with('post', object, template)
181
+ .and_return(input_builder)
182
+ allow(input_builder).to receive(:build_editor)
183
+ end
184
+
185
+ it 'creates EditorInputBuilder with correct parameters' do
186
+ builder.ckeditor5(:content, class: 'custom-class')
187
+
188
+ expect(CKEditor5::Rails::Hooks::Form::EditorInputBuilder)
189
+ .to have_received(:new)
190
+ .with('post', object, template)
191
+ end
192
+
193
+ it 'calls build_editor with correct parameters' do
194
+ options = { class: 'custom-class' }
195
+ builder.ckeditor5(:content, options)
196
+
197
+ expect(input_builder)
198
+ .to have_received(:build_editor)
199
+ .with(:content, options)
200
+ end
45
201
  end
46
202
  end
47
203
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CKEditor5::Rails::Hooks::SimpleForm::CKEditor5Input do
6
+ let(:template) { instance_double('ActionView::Base') }
7
+ let(:object) { double('Post', content: 'Initial content') }
8
+ let(:object_name) { 'post' }
9
+ let(:builder) do
10
+ double('FormBuilder',
11
+ object: object,
12
+ object_name: object_name,
13
+ template: template)
14
+ end
15
+ let(:attribute_name) { :content }
16
+ let(:input_html_options) { {} }
17
+ let(:input_options) { {} }
18
+
19
+ subject(:input) do
20
+ described_class.new(builder, attribute_name, nil, input_html_options, input_options)
21
+ end
22
+
23
+ before do
24
+ allow(template).to receive(:ckeditor5_editor)
25
+ end
26
+
27
+ describe '#input' do
28
+ it 'renders ckeditor with default options' do
29
+ input.input
30
+
31
+ expect(template).to have_received(:ckeditor5_editor).with(
32
+ hash_including(
33
+ preset: :default,
34
+ type: :classic,
35
+ config: nil,
36
+ initial_data: 'Initial content',
37
+ name: 'post[content]',
38
+ class: [{}, :required] # Simple Form adds these classes by default
39
+ )
40
+ )
41
+ end
42
+
43
+ context 'with custom options' do
44
+ let(:input_options) do
45
+ {
46
+ preset: :custom_preset,
47
+ type: :inline,
48
+ config: { toolbar: [:bold] }
49
+ }
50
+ end
51
+ let(:input_html_options) do
52
+ { class: 'custom-class', id: 'custom-id' }
53
+ end
54
+
55
+ it 'renders ckeditor with merged options' do
56
+ input.input
57
+
58
+ expect(template).to have_received(:ckeditor5_editor).with(
59
+ hash_including(
60
+ preset: :custom_preset,
61
+ type: :inline,
62
+ config: { toolbar: [:bold] },
63
+ initial_data: 'Initial content',
64
+ name: 'post[content]',
65
+ class: [{ class: 'custom-class', id: 'custom-id' }, :required]
66
+ )
67
+ )
68
+ end
69
+ end
70
+
71
+ context 'when object does not respond to attribute' do
72
+ let(:object) { double('Post') }
73
+ let(:input_options) { { initial_data: 'Provided content' } }
74
+
75
+ it 'uses initial_data from options' do
76
+ input.input
77
+
78
+ expect(template).to have_received(:ckeditor5_editor).with(
79
+ hash_including(initial_data: 'Provided content')
80
+ )
81
+ end
82
+ end
83
+
84
+ context 'with wrapper options' do
85
+ let(:wrapper_options) { { wrapper_class: 'wrapper' } }
86
+ let(:input_html_options) { { class: 'input' } }
87
+
88
+ it 'merges wrapper options with input options' do
89
+ input.input(wrapper_options)
90
+
91
+ expect(template).to have_received(:ckeditor5_editor).with(
92
+ hash_including(
93
+ class: [{ class: 'input' }, :required],
94
+ wrapper_class: 'wrapper'
95
+ )
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end