cad0947dcf9996f37e2508d9103f0e379e919439
[yaffs-website] / web / core / modules / ckeditor / tests / src / Functional / CKEditorLoadingTest.php
1 <?php
2
3 namespace Drupal\Tests\ckeditor\Functional;
4
5 use Drupal\editor\Entity\Editor;
6 use Drupal\filter\Entity\FilterFormat;
7 use Drupal\Tests\BrowserTestBase;
8
9 /**
10  * Tests loading of CKEditor.
11  *
12  * @group ckeditor
13  */
14 class CKEditorLoadingTest extends BrowserTestBase {
15
16   /**
17    * Modules to enable.
18    *
19    * @var array
20    */
21   public static $modules = ['filter', 'editor', 'ckeditor', 'node'];
22
23   /**
24    * An untrusted user with access to only the 'plain_text' format.
25    *
26    * @var \Drupal\user\UserInterface
27    */
28   protected $untrustedUser;
29
30   /**
31    * A normal user with access to the 'plain_text' and 'filtered_html' formats.
32    *
33    * @var \Drupal\user\UserInterface
34    */
35   protected $normalUser;
36
37   protected function setUp() {
38     parent::setUp();
39
40     // Create text format, associate CKEditor.
41     $filtered_html_format = FilterFormat::create([
42       'format' => 'filtered_html',
43       'name' => 'Filtered HTML',
44       'weight' => 0,
45       'filters' => [],
46     ]);
47     $filtered_html_format->save();
48     $editor = Editor::create([
49       'format' => 'filtered_html',
50       'editor' => 'ckeditor',
51     ]);
52     $editor->save();
53
54     // Create a second format without an associated editor so a drop down select
55     // list is created when selecting formats.
56     $full_html_format = FilterFormat::create([
57       'format' => 'full_html',
58       'name' => 'Full HTML',
59       'weight' => 1,
60       'filters' => [],
61     ]);
62     $full_html_format->save();
63
64     // Create node type.
65     $this->drupalCreateContentType([
66       'type' => 'article',
67       'name' => 'Article',
68     ]);
69
70     $this->untrustedUser = $this->drupalCreateUser(['create article content', 'edit any article content']);
71     $this->normalUser = $this->drupalCreateUser(['create article content', 'edit any article content', 'use text format filtered_html', 'use text format full_html']);
72   }
73
74   /**
75    * Tests loading of CKEditor CSS, JS and JS settings.
76    */
77   public function testLoading() {
78     // The untrusted user:
79     // - has access to 1 text format (plain_text);
80     // - doesn't have access to the filtered_html text format, so: no text editor.
81     $this->drupalLogin($this->untrustedUser);
82     $this->drupalGet('node/add/article');
83     list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck();
84     $this->assertFalse($editor_settings_present, 'No Text Editor module settings.');
85     $this->assertFalse($editor_js_present, 'No Text Editor JavaScript.');
86     $this->assertTrue(count($body) === 1, 'A body field exists.');
87     $this->assertTrue(count($format_selector) === 0, 'No text format selector exists on the page.');
88     $hidden_input = $this->xpath('//input[@type="hidden" and contains(@class, "editor")]');
89     $this->assertTrue(count($hidden_input) === 0, 'A single text format hidden input does not exist on the page.');
90     $this->assertNoRaw(drupal_get_path('module', 'ckeditor') . '/js/ckeditor.js', 'CKEditor glue JS is absent.');
91
92     // On pages where there would never be a text editor, CKEditor JS is absent.
93     $this->drupalGet('user');
94     $this->assertNoRaw(drupal_get_path('module', 'ckeditor') . '/js/ckeditor.js', 'CKEditor glue JS is absent.');
95
96     // The normal user:
97     // - has access to 2 text formats;
98     // - does have access to the filtered_html text format, so: CKEditor.
99     $this->drupalLogin($this->normalUser);
100     $this->drupalGet('node/add/article');
101     list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck();
102     $ckeditor_plugin = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
103     $editor = Editor::load('filtered_html');
104     $expected = [
105       'formats' => [
106         'filtered_html' => [
107           'format' => 'filtered_html',
108           'editor' => 'ckeditor',
109           'editorSettings' => $this->castSafeStrings($ckeditor_plugin->getJSSettings($editor)),
110           'editorSupportsContentFiltering' => TRUE,
111           'isXssSafe' => FALSE,
112         ],
113       ],
114     ];
115     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
116     $this->assertIdentical($expected, $this->castSafeStrings($settings['editor']), "Text Editor module's JavaScript settings on the page are correct.");
117     $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
118     $this->assertTrue(count($body) === 1, 'A body field exists.');
119     $this->assertTrue(count($format_selector) === 1, 'A single text format selector exists on the page.');
120     $specific_format_selector = $this->xpath('//select[contains(@class, "filter-list") and @data-editor-for="edit-body-0-value"]');
121     $this->assertTrue(count($specific_format_selector) === 1, 'A single text format selector exists on the page and has a "data-editor-for" attribute with the correct value.');
122     $this->assertTrue(in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])), 'CKEditor glue library is present.');
123
124     // Enable the ckeditor_test module, customize configuration. In this case,
125     // there is additional CSS and JS to be loaded.
126     // NOTE: the tests in CKEditorTest already ensure that changing the
127     // configuration also results in modified CKEditor configuration, so we
128     // don't test that here.
129     \Drupal::service('module_installer')->install(['ckeditor_test']);
130     $this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
131     $editor_settings = $editor->getSettings();
132     $editor_settings['toolbar']['rows'][0][0]['items'][] = 'Llama';
133     $editor->setSettings($editor_settings);
134     $editor->save();
135     $this->drupalGet('node/add/article');
136     list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck();
137     $expected = [
138       'formats' => [
139         'filtered_html' => [
140           'format' => 'filtered_html',
141           'editor' => 'ckeditor',
142           'editorSettings' => $this->castSafeStrings($ckeditor_plugin->getJSSettings($editor)),
143           'editorSupportsContentFiltering' => TRUE,
144           'isXssSafe' => FALSE,
145         ],
146       ],
147     ];
148     $this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
149     $this->assertIdentical($expected, $this->castSafeStrings($settings['editor']), "Text Editor module's JavaScript settings on the page are correct.");
150     $this->assertTrue($editor_js_present, 'Text Editor JavaScript is present.');
151     $this->assertTrue(in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])), 'CKEditor glue library is present.');
152
153     // Assert that CKEditor uses Drupal's cache-busting query string by
154     // comparing the setting sent with the page with the current query string.
155     $settings = $this->getDrupalSettings();
156     $expected = $settings['ckeditor']['timestamp'];
157     $this->assertIdentical($expected, \Drupal::state()->get('system.css_js_query_string'), "CKEditor scripts cache-busting string is correct before flushing all caches.");
158     // Flush all caches then make sure that $settings['ckeditor']['timestamp']
159     // still matches.
160     drupal_flush_all_caches();
161     $this->assertIdentical($expected, \Drupal::state()->get('system.css_js_query_string'), "CKEditor scripts cache-busting string is correct after flushing all caches.");
162   }
163
164   /**
165    * Tests presence of essential configuration even without Internal's buttons.
166    */
167   public function testLoadingWithoutInternalButtons() {
168     // Change the CKEditor text editor configuration to only have link buttons.
169     // This means:
170     // - 0 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal
171     // - 2 buttons are from \Drupal\ckeditor\Plugin\CKEditorPlugin\DrupalLink
172     $filtered_html_editor = Editor::load('filtered_html');
173     $settings = $filtered_html_editor->getSettings();
174     $settings['toolbar']['rows'] = [
175       0 => [
176         0 => [
177           'name' => 'Links',
178           'items' => [
179             'DrupalLink',
180             'DrupalUnlink',
181           ],
182         ],
183       ],
184     ];
185     $filtered_html_editor->setSettings($settings)->save();
186
187     // Even when no buttons of \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal
188     // are in use, its configuration (Internal::getConfig()) is still essential:
189     // this is configuration that is associated with the (custom, optimized)
190     // build of CKEditor that Drupal core ships with. For example, it configures
191     // CKEditor to not perform its default action of loading a config.js file,
192     // to not convert special characters into HTML entities, and the allowedContent
193     // setting to configure CKEditor's Advanced Content Filter.
194     $this->drupalLogin($this->normalUser);
195     $this->drupalGet('node/add/article');
196     $editor_settings = $this->getDrupalSettings()['editor']['formats']['filtered_html']['editorSettings'];
197     $this->assertTrue(isset($editor_settings['customConfig']));
198     $this->assertTrue(isset($editor_settings['entities']));
199     $this->assertTrue(isset($editor_settings['allowedContent']));
200     $this->assertTrue(isset($editor_settings['disallowedContent']));
201   }
202
203   /**
204    * Tests loading of theme's CKEditor stylesheets defined in the .info file.
205    */
206   public function testExternalStylesheets() {
207     $theme_handler = \Drupal::service('theme_handler');
208     // Case 1: Install theme which has an absolute external CSS URL.
209     $theme_handler->install(['test_ckeditor_stylesheets_external']);
210     $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_external')->save();
211     $expected = [
212       'https://fonts.googleapis.com/css?family=Open+Sans',
213     ];
214     $this->assertIdentical($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_external'));
215
216     // Case 2: Install theme which has an external protocol-relative CSS URL.
217     $theme_handler->install(['test_ckeditor_stylesheets_protocol_relative']);
218     $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_protocol_relative')->save();
219     $expected = [
220       '//fonts.googleapis.com/css?family=Open+Sans',
221     ];
222     $this->assertIdentical($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_protocol_relative'));
223
224     // Case 3: Install theme which has a relative CSS URL.
225     $theme_handler->install(['test_ckeditor_stylesheets_relative']);
226     $this->config('system.theme')->set('default', 'test_ckeditor_stylesheets_relative')->save();
227     $expected = [
228       'core/modules/system/tests/themes/test_ckeditor_stylesheets_relative/css/yokotsoko.css',
229     ];
230     $this->assertIdentical($expected, _ckeditor_theme_css('test_ckeditor_stylesheets_relative'));
231   }
232
233   protected function getThingsToCheck() {
234     $settings = $this->getDrupalSettings();
235     return [
236       // JavaScript settings.
237       $settings,
238       // Editor.module's JS settings present.
239       isset($settings['editor']),
240       // Editor.module's JS present. Note: ckeditor/drupal.ckeditor depends on
241       // editor/drupal.editor, hence presence of the former implies presence of
242       // the latter.
243       isset($settings['ajaxPageState']['libraries']) && in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])),
244       // Body field.
245       $this->xpath('//textarea[@id="edit-body-0-value"]'),
246       // Format selector.
247       $this->xpath('//select[contains(@class, "filter-list")]'),
248     ];
249   }
250
251 }