ckeditor5 1.15.10 → 1.16.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1cdcc72197788bc6dc57bb3b01158c932dbf27621084414bc832c5d772e01701
4
- data.tar.gz: 7d6874e08d93346f9d478f46e2240bbc5df78a32700f50087de8fe8e4301cfc8
3
+ metadata.gz: 95f068ef75561d88a26a68c5aafa6e6f86b55405984e83bf35909085f288f9ba
4
+ data.tar.gz: d72e733641c5ac8c4f9d1e20731d54ffd2c0e4d88bc3b82618eb62b8c2820049
5
5
  SHA512:
6
- metadata.gz: 7dc9311974b2dd72a05fd61cea086585179e64700f800618d06f4bffd079f31ad99d31f1ea5917d7c28cdd4e03d675122b546d25518ed43a99928f2061afd448
7
- data.tar.gz: 199da996b61646c658dbbe0c8119419220b669442a5eba6fc1c973dad7793e3db8e3a1e8808e075e8d0477236fd8741fa2927eae9c141fc7bb87364df6fee7ae
6
+ metadata.gz: 31fb63fa1ac20061e1c28150178b7b8f92ca54e99073f2e180a7823d7f76f58c75a9f55e70b9a6f729d9715389b1a349305027e6d6c0c56bb2f7539d1a6bc0ad
7
+ data.tar.gz: 4019ffcc8e326be64930092815d5ff68436e4c242a67853adf735b4667aa7bec624df66fe9614d0f7e4bb00c67c2c0cf0e1195c43647d397b9f99ab8a0637479
data/Gemfile CHANGED
@@ -24,6 +24,7 @@ end
24
24
 
25
25
  group :test, :development do
26
26
  gem 'capybara', '~> 3.40'
27
+ gem 'cuprite', '~> 0.15.0'
27
28
  gem 'rspec', '~> 3.13'
28
29
  gem 'rspec-expectations', '~> 3.13'
29
30
  gem 'rspec-html-matchers', '~> 0.10.0'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.15.10'
5
+ VERSION = '1.16.1'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '43.3.1'
8
8
  end
@@ -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.15.10
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