ckeditor5 1.15.9 → 1.16.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/context.mjs +23 -0
- data/lib/ckeditor5/rails/assets/webcomponents/components/editor.mjs +24 -0
- data/lib/ckeditor5/rails/assets/webcomponents/utils.mjs +33 -10
- data/lib/ckeditor5/rails/concerns/checksum.rb +15 -0
- data/lib/ckeditor5/rails/context/props.rb +17 -2
- data/lib/ckeditor5/rails/editor/props.rb +16 -3
- data/lib/ckeditor5/rails/editor/props_base_plugin.rb +19 -0
- data/lib/ckeditor5/rails/editor/props_inline_plugin.rb +6 -3
- data/lib/ckeditor5/rails/editor/props_plugin.rb +6 -4
- data/lib/ckeditor5/rails/engine.rb +3 -2
- data/lib/ckeditor5/rails/presets/plugins_builder.rb +9 -9
- data/lib/ckeditor5/rails/presets/preset_builder.rb +4 -6
- data/lib/ckeditor5/rails/version.rb +1 -1
- data/lib/ckeditor5/rails.rb +1 -0
- data/spec/e2e/features/editor_types_spec.rb +178 -0
- data/spec/e2e/features/form_integration_spec.rb +60 -0
- data/spec/e2e/spec_helper.rb +43 -0
- data/spec/e2e/support/eventually.rb +14 -0
- data/spec/e2e/support/form_helpers.rb +37 -0
- data/spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb +7 -0
- data/spec/lib/ckeditor5/rails/cdn/helpers_spec.rb +19 -1
- data/spec/lib/ckeditor5/rails/concerns/checksum_spec.rb +50 -0
- data/spec/lib/ckeditor5/rails/context/props_spec.rb +7 -1
- data/spec/lib/ckeditor5/rails/editor/props_base_plugin_spec.rb +27 -0
- data/spec/lib/ckeditor5/rails/editor/props_spec.rb +2 -0
- data/spec/lib/ckeditor5/rails/engine_spec.rb +88 -0
- data/spec/lib/ckeditor5/rails/hooks/form_spec.rb +156 -0
- data/spec/lib/ckeditor5/rails/hooks/simple_form_spec.rb +100 -0
- data/spec/lib/ckeditor5/rails/presets/plugins_builder_spec.rb +10 -10
- data/spec/lib/ckeditor5/rails/presets/preset_builder_spec.rb +10 -0
- data/spec/spec_helper.rb +2 -2
- 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
|
-
|
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
|