ckeditor5 1.15.10 → 1.16.1
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/version.rb +1 -1
- 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/spec_helper.rb +1 -1
- metadata +11 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95f068ef75561d88a26a68c5aafa6e6f86b55405984e83bf35909085f288f9ba
|
4
|
+
data.tar.gz: d72e733641c5ac8c4f9d1e20731d54ffd2c0e4d88bc3b82618eb62b8c2820049
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31fb63fa1ac20061e1c28150178b7b8f92ca54e99073f2e180a7823d7f76f58c75a9f55e70b9a6f729d9715389b1a349305027e6d6c0c56bb2f7539d1a6bc0ad
|
7
|
+
data.tar.gz: 4019ffcc8e326be64930092815d5ff68436e4c242a67853adf735b4667aa7bec624df66fe9614d0f7e4bb00c67c2c0cf0e1195c43647d397b9f99ab8a0637479
|
data/Gemfile
CHANGED
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'e2e/spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe 'CKEditor5 Types Integration', type: :feature, js: true do
|
6
|
+
shared_examples 'an editor' do |path|
|
7
|
+
before { visit path }
|
8
|
+
|
9
|
+
it 'loads and initializes the editor' do
|
10
|
+
expect(page).to have_css('.ck-editor__editable', wait: 10)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
shared_examples 'an editor that fires change event with main payload' do |path|
|
15
|
+
before { visit path }
|
16
|
+
|
17
|
+
it 'sends properly change events with proper payload' do
|
18
|
+
editor = first('.ck-editor__editable')
|
19
|
+
|
20
|
+
# Set up detailed change event listener
|
21
|
+
page.execute_script(<<~JS)
|
22
|
+
window._editorEvents = [];
|
23
|
+
document.querySelector('ckeditor-component').addEventListener('editor-change', (e) => {
|
24
|
+
window._editorEvents.push({
|
25
|
+
data: e.detail.data,
|
26
|
+
hasEditor: !!e.detail.editor
|
27
|
+
});
|
28
|
+
});
|
29
|
+
JS
|
30
|
+
|
31
|
+
# Clear editor and type text
|
32
|
+
editor.click
|
33
|
+
editor.send_keys([[:control, 'a'], :backspace])
|
34
|
+
editor.send_keys('Hello from keyboard!')
|
35
|
+
|
36
|
+
# Wait for change events and verify the last one
|
37
|
+
eventually do
|
38
|
+
events = page.evaluate_script('window._editorEvents')
|
39
|
+
last_event = events.last
|
40
|
+
|
41
|
+
expect(last_event['data']).to eq('main' => '<p>Hello from keyboard!</p>')
|
42
|
+
expect(last_event['hasEditor']).to be true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
shared_examples 'a multiroot editor that fires change events' do |path, editables| # rubocop:disable Metrics/BlockLength
|
48
|
+
before { visit path }
|
49
|
+
|
50
|
+
it 'sends properly change events with proper payload for editables' do # rubocop:disable Metrics/BlockLength
|
51
|
+
editors = editables.map do |name|
|
52
|
+
find("[data-testid='#{name}-editable']")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Set up detailed change event listener
|
56
|
+
page.execute_script(<<~JS)
|
57
|
+
window._editorEvents = [];
|
58
|
+
document.querySelector('ckeditor-component').addEventListener('editor-change', (e) => {
|
59
|
+
window._editorEvents.push({
|
60
|
+
data: e.detail.data,
|
61
|
+
hasEditor: !!e.detail.editor
|
62
|
+
});
|
63
|
+
});
|
64
|
+
JS
|
65
|
+
|
66
|
+
# Test each editable
|
67
|
+
expected_data = {}
|
68
|
+
editors.each_with_index do |editor, index|
|
69
|
+
editor.click
|
70
|
+
editor.send_keys([[:control, 'a'], :backspace])
|
71
|
+
content = "Content for #{editables[index]}"
|
72
|
+
editor.send_keys(content)
|
73
|
+
expected_data[editables[index]] = "<p>#{content}</p>"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Wait for change events and verify the last one
|
77
|
+
eventually do
|
78
|
+
events = page.evaluate_script('window._editorEvents')
|
79
|
+
last_event = events.last
|
80
|
+
|
81
|
+
expect(last_event['data']).to eq(expected_data)
|
82
|
+
expect(last_event['hasEditor']).to be true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'Classic Editor' do
|
88
|
+
it_behaves_like 'an editor', 'classic'
|
89
|
+
it_behaves_like 'an editor that fires change event with main payload', 'classic'
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'Decoupled Editor' do
|
93
|
+
before { visit 'decoupled' }
|
94
|
+
|
95
|
+
it_behaves_like 'an editor', 'decoupled'
|
96
|
+
it_behaves_like 'an editor that fires change event with main payload', 'decoupled'
|
97
|
+
|
98
|
+
it 'has separate toolbar' do
|
99
|
+
expect(page).to have_css('.toolbar-container .ck-toolbar')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'Balloon Editor' do
|
104
|
+
before { visit 'balloon' }
|
105
|
+
|
106
|
+
it_behaves_like 'an editor', 'balloon'
|
107
|
+
it_behaves_like 'an editor that fires change event with main payload', 'balloon'
|
108
|
+
|
109
|
+
it 'shows balloon toolbar on selection' do
|
110
|
+
editor = first('.ck-editor__editable')
|
111
|
+
editor.click
|
112
|
+
|
113
|
+
expect(page).to have_css('.ck-balloon-panel', wait: 5)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'Inline Editor' do
|
118
|
+
it_behaves_like 'an editor', 'inline'
|
119
|
+
it_behaves_like 'an editor that fires change event with main payload', 'inline'
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'Multiroot Editor' do
|
123
|
+
before { visit 'multiroot' }
|
124
|
+
|
125
|
+
it_behaves_like 'an editor', 'multiroot'
|
126
|
+
it_behaves_like 'a multiroot editor that fires change events', 'multiroot', %w[toolbar content]
|
127
|
+
|
128
|
+
it 'supports multiple editable areas' do
|
129
|
+
expect(page).to have_css('.ck-editor__editable', minimum: 2)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'shares toolbar between editables' do
|
133
|
+
expect(page).to have_css('.ck-toolbar', count: 1)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'handles dynamically added editables' do # rubocop:disable Metrics/BlockLength
|
137
|
+
# Set up event listener
|
138
|
+
page.execute_script(<<~JS)
|
139
|
+
window._newEditableEvents = [];
|
140
|
+
document.querySelector('ckeditor-component').addEventListener('editor-change', (e) => {
|
141
|
+
window._newEditableEvents.push({
|
142
|
+
data: e.detail.data,
|
143
|
+
hasEditor: !!e.detail.editor
|
144
|
+
});
|
145
|
+
});
|
146
|
+
JS
|
147
|
+
|
148
|
+
# Add new editable component
|
149
|
+
page.execute_script(<<~JS)
|
150
|
+
const container = document.querySelector('[data-testid="multiroot-editor"]');
|
151
|
+
const newEditable = document.createElement('ckeditor-editable-component');
|
152
|
+
newEditable.setAttribute('name', 'new-root');
|
153
|
+
container.appendChild(newEditable);
|
154
|
+
JS
|
155
|
+
|
156
|
+
sleep 0.1 # Wait for component initialization
|
157
|
+
|
158
|
+
# Find and interact with new editable
|
159
|
+
new_editable = find("[name='new-root']")
|
160
|
+
new_editable.click
|
161
|
+
new_editable.send_keys('Content for new root')
|
162
|
+
|
163
|
+
# Verify the change event
|
164
|
+
eventually do
|
165
|
+
events = page.evaluate_script('window._newEditableEvents')
|
166
|
+
last_event = events.last
|
167
|
+
|
168
|
+
expect(last_event['data']).to include(
|
169
|
+
'content' => '',
|
170
|
+
'new-root' => '<p>Content for new root</p>',
|
171
|
+
'toolbar' => '<p>This is a toolbar editable</p>'
|
172
|
+
)
|
173
|
+
|
174
|
+
expect(last_event['hasEditor']).to be true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -36,7 +36,7 @@ require 'rspec-html-matchers'
|
|
36
36
|
|
37
37
|
Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require f }
|
38
38
|
|
39
|
-
Rails.application.initialize!
|
39
|
+
Rails.application.initialize! unless Rails.application.initialized?
|
40
40
|
|
41
41
|
RSpec.configure do |config|
|
42
42
|
config.expect_with :rspec do |expectations|
|
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.
|
4
|
+
version: 1.16.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mateusz Bagiński
|
@@ -76,6 +76,11 @@ files:
|
|
76
76
|
- lib/ckeditor5/rails/semver.rb
|
77
77
|
- lib/ckeditor5/rails/version.rb
|
78
78
|
- lib/ckeditor5/rails/version_detector.rb
|
79
|
+
- spec/e2e/features/editor_types_spec.rb
|
80
|
+
- spec/e2e/features/form_integration_spec.rb
|
81
|
+
- spec/e2e/spec_helper.rb
|
82
|
+
- spec/e2e/support/eventually.rb
|
83
|
+
- spec/e2e/support/form_helpers.rb
|
79
84
|
- spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb
|
80
85
|
- spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb
|
81
86
|
- spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb
|
@@ -126,6 +131,11 @@ signing_key:
|
|
126
131
|
specification_version: 4
|
127
132
|
summary: CKEditor 5 for Rails
|
128
133
|
test_files:
|
134
|
+
- spec/e2e/features/editor_types_spec.rb
|
135
|
+
- spec/e2e/features/form_integration_spec.rb
|
136
|
+
- spec/e2e/spec_helper.rb
|
137
|
+
- spec/e2e/support/eventually.rb
|
138
|
+
- spec/e2e/support/form_helpers.rb
|
129
139
|
- spec/lib/ckeditor5/rails/assets/asset_bundle_hml_serializer_spec.rb
|
130
140
|
- spec/lib/ckeditor5/rails/assets/assets_bundle_spec.rb
|
131
141
|
- spec/lib/ckeditor5/rails/cdn/ckbox_bundle_spec.rb
|