Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / system / src / Tests / Ajax / FrameworkTest.php
1 <?php
2
3 namespace Drupal\system\Tests\Ajax;
4
5 use Drupal\Core\Ajax\AddCssCommand;
6 use Drupal\Core\Ajax\AlertCommand;
7 use Drupal\Core\Ajax\AppendCommand;
8 use Drupal\Core\Ajax\HtmlCommand;
9 use Drupal\Core\Ajax\PrependCommand;
10 use Drupal\Core\Ajax\SettingsCommand;
11 use Drupal\Core\Asset\AttachedAssets;
12
13 /**
14  * Performs tests on AJAX framework functions.
15  *
16  * @group Ajax
17  */
18 class FrameworkTest extends AjaxTestBase {
19
20   /**
21    * Verifies the Ajax rendering of a command in the settings.
22    */
23   public function testAJAXRender() {
24     // Verify that settings command is generated if JavaScript settings exist.
25     $commands = $this->drupalGetAjax('ajax-test/render');
26     $expected = new SettingsCommand(['ajax' => 'test'], TRUE);
27     $this->assertCommand($commands, $expected->render(), 'JavaScript settings command is present.');
28   }
29
30   /**
31    * Tests AjaxResponse::prepare() AJAX commands ordering.
32    */
33   public function testOrder() {
34     $expected_commands = [];
35
36     // Expected commands, in a very specific order.
37     $asset_resolver = \Drupal::service('asset.resolver');
38     $css_collection_renderer = \Drupal::service('asset.css.collection_renderer');
39     $js_collection_renderer = \Drupal::service('asset.js.collection_renderer');
40     $renderer = \Drupal::service('renderer');
41     $expected_commands[0] = new SettingsCommand(['ajax' => 'test'], TRUE);
42     $build['#attached']['library'][] = 'ajax_test/order-css-command';
43     $assets = AttachedAssets::createFromRenderArray($build);
44     $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE));
45     $expected_commands[1] = new AddCssCommand($renderer->renderRoot($css_render_array));
46     $build['#attached']['library'][] = 'ajax_test/order-header-js-command';
47     $build['#attached']['library'][] = 'ajax_test/order-footer-js-command';
48     $assets = AttachedAssets::createFromRenderArray($build);
49     list($js_assets_header, $js_assets_footer) = $asset_resolver->getJsAssets($assets, FALSE);
50     $js_header_render_array = $js_collection_renderer->render($js_assets_header);
51     $js_footer_render_array = $js_collection_renderer->render($js_assets_footer);
52     $expected_commands[2] = new PrependCommand('head', $js_header_render_array);
53     $expected_commands[3] = new AppendCommand('body', $js_footer_render_array);
54     $expected_commands[4] = new HtmlCommand('body', 'Hello, world!');
55
56     // Load any page with at least one CSS file, at least one JavaScript file
57     // and at least one #ajax-powered element. The latter is an assumption of
58     // drupalPostAjaxForm(), the two former are assumptions of the Ajax
59     // renderer.
60     // @todo refactor AJAX Framework + tests to make less assumptions.
61     $this->drupalGet('ajax_forms_test_lazy_load_form');
62
63     // Verify AJAX command order — this should always be the order:
64     // 1. JavaScript settings
65     // 2. CSS files
66     // 3. JavaScript files in the header
67     // 4. JavaScript files in the footer
68     // 5. Any other AJAX commands, in whatever order they were added.
69     $commands = $this->drupalPostAjaxForm(NULL, [], NULL, 'ajax-test/order', [], [], NULL, []);
70     $this->assertCommand(array_slice($commands, 0, 1), $expected_commands[0]->render(), 'Settings command is first.');
71     $this->assertCommand(array_slice($commands, 1, 1), $expected_commands[1]->render(), 'CSS command is second (and CSS files are ordered correctly).');
72     $this->assertCommand(array_slice($commands, 2, 1), $expected_commands[2]->render(), 'Header JS command is third.');
73     $this->assertCommand(array_slice($commands, 3, 1), $expected_commands[3]->render(), 'Footer JS command is fourth.');
74     $this->assertCommand(array_slice($commands, 4, 1), $expected_commands[4]->render(), 'HTML command is fifth.');
75   }
76
77   /**
78    * Tests the behavior of an error alert command.
79    */
80   public function testAJAXRenderError() {
81     // Verify custom error message.
82     $edit = [
83       'message' => 'Custom error message.',
84     ];
85     $commands = $this->drupalGetAjax('ajax-test/render-error', ['query' => $edit]);
86     $expected = new AlertCommand($edit['message']);
87     $this->assertCommand($commands, $expected->render(), 'Custom error message is output.');
88   }
89
90   /**
91    * Tests that new JavaScript and CSS files are lazy-loaded on an AJAX request.
92    */
93   public function testLazyLoad() {
94     $asset_resolver = \Drupal::service('asset.resolver');
95     $css_collection_renderer = \Drupal::service('asset.css.collection_renderer');
96     $js_collection_renderer = \Drupal::service('asset.js.collection_renderer');
97     $renderer = \Drupal::service('renderer');
98
99     $expected = [
100       'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
101       'setting_value' => 'executed',
102       'library_1' => 'system/admin',
103       'library_2' => 'system/drupal.system',
104     ];
105
106     // Get the base page.
107     $this->drupalGet('ajax_forms_test_lazy_load_form');
108     $original_settings = $this->getDrupalSettings();
109     $original_libraries = explode(',', $original_settings['ajaxPageState']['libraries']);
110
111     // Verify that the base page doesn't have the settings and files that are to
112     // be lazy loaded as part of the next requests.
113     $this->assertTrue(!isset($original_settings[$expected['setting_name']]), format_string('Page originally lacks the %setting, as expected.', ['%setting' => $expected['setting_name']]));
114     $this->assertTrue(!in_array($expected['library_1'], $original_libraries), format_string('Page originally lacks the %library library, as expected.', ['%library' => $expected['library_1']]));
115     $this->assertTrue(!in_array($expected['library_2'], $original_libraries), format_string('Page originally lacks the %library library, as expected.', ['%library' => $expected['library_2']]));
116
117     // Calculate the expected CSS and JS.
118     $assets = new AttachedAssets();
119     $assets->setLibraries([$expected['library_1']])
120       ->setAlreadyLoadedLibraries($original_libraries);
121     $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE));
122     $expected_css_html = $renderer->renderRoot($css_render_array);
123
124     $assets->setLibraries([$expected['library_2']])
125       ->setAlreadyLoadedLibraries($original_libraries);
126     $js_assets = $asset_resolver->getJsAssets($assets, FALSE)[1];
127     unset($js_assets['drupalSettings']);
128     $js_render_array = $js_collection_renderer->render($js_assets);
129     $expected_js_html = $renderer->renderRoot($js_render_array);
130
131     // Submit the AJAX request without triggering files getting added.
132     $commands = $this->drupalPostAjaxForm(NULL, ['add_files' => FALSE], ['op' => t('Submit')]);
133     $new_settings = $this->getDrupalSettings();
134     $new_libraries = explode(',', $new_settings['ajaxPageState']['libraries']);
135
136     // Verify the setting was not added when not expected.
137     $this->assertTrue(!isset($new_settings[$expected['setting_name']]), format_string('Page still lacks the %setting, as expected.', ['%setting' => $expected['setting_name']]));
138     $this->assertTrue(!in_array($expected['library_1'], $new_libraries), format_string('Page still lacks the %library library, as expected.', ['%library' => $expected['library_1']]));
139     $this->assertTrue(!in_array($expected['library_2'], $new_libraries), format_string('Page still lacks the %library library, as expected.', ['%library' => $expected['library_2']]));
140     // Verify a settings command does not add CSS or scripts to drupalSettings
141     // and no command inserts the corresponding tags on the page.
142     $found_settings_command = FALSE;
143     $found_markup_command = FALSE;
144     foreach ($commands as $command) {
145       if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
146         $found_settings_command = TRUE;
147       }
148       if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
149         $found_markup_command = TRUE;
150       }
151     }
152     $this->assertFalse($found_settings_command, format_string('Page state still lacks the %library_1 and %library_2 libraries, as expected.', ['%library_1' => $expected['library_1'], '%library_2' => $expected['library_2']]));
153     $this->assertFalse($found_markup_command, format_string('Page still lacks the %library_1 and %library_2 libraries, as expected.', ['%library_1' => $expected['library_1'], '%library_2' => $expected['library_2']]));
154
155     // Submit the AJAX request and trigger adding files.
156     $commands = $this->drupalPostAjaxForm(NULL, ['add_files' => TRUE], ['op' => t('Submit')]);
157     $new_settings = $this->getDrupalSettings();
158     $new_libraries = explode(',', $new_settings['ajaxPageState']['libraries']);
159
160     // Verify the expected setting was added, both to drupalSettings, and as
161     // the first AJAX command.
162     $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], format_string('Page now has the %setting.', ['%setting' => $expected['setting_name']]));
163     $expected_command = new SettingsCommand([$expected['setting_name'] => $expected['setting_value']], TRUE);
164     $this->assertCommand(array_slice($commands, 0, 1), $expected_command->render(), 'The settings command was first.');
165
166     // Verify the expected CSS file was added, both to drupalSettings, and as
167     // the second AJAX command for inclusion into the HTML.
168     $this->assertTrue(in_array($expected['library_1'], $new_libraries), format_string('Page state now has the %library library.', ['%library' => $expected['library_1']]));
169     $this->assertCommand(array_slice($commands, 1, 1), ['data' => $expected_css_html], format_string('Page now has the %library library.', ['%library' => $expected['library_1']]));
170
171     // Verify the expected JS file was added, both to drupalSettings, and as
172     // the third AJAX command for inclusion into the HTML. By testing for an
173     // exact HTML string containing the SCRIPT tag, we also ensure that
174     // unexpected JavaScript code, such as a jQuery.extend() that would
175     // potentially clobber rather than properly merge settings, didn't
176     // accidentally get added.
177     $this->assertTrue(in_array($expected['library_2'], $new_libraries), format_string('Page state now has the %library library.', ['%library' => $expected['library_2']]));
178     $this->assertCommand(array_slice($commands, 2, 1), ['data' => $expected_js_html], format_string('Page now has the %library library.', ['%library' => $expected['library_2']]));
179   }
180
181   /**
182    * Tests that drupalSettings.currentPath is not updated on AJAX requests.
183    */
184   public function testCurrentPathChange() {
185     $commands = $this->drupalPostAjaxForm('ajax_forms_test_lazy_load_form', ['add_files' => FALSE], ['op' => t('Submit')]);
186     foreach ($commands as $command) {
187       if ($command['command'] == 'settings') {
188         $this->assertFalse(isset($command['settings']['currentPath']), 'Value of drupalSettings.currentPath is not updated after an AJAX request.');
189       }
190     }
191   }
192
193   /**
194    * Tests that overridden CSS files are not added during lazy load.
195    */
196   public function testLazyLoadOverriddenCSS() {
197     // The test theme overrides js.module.css without an implementation,
198     // thereby removing it.
199     \Drupal::service('theme_handler')->install(['test_theme']);
200     $this->config('system.theme')
201       ->set('default', 'test_theme')
202       ->save();
203
204     // This gets the form, and emulates an Ajax submission on it, including
205     // adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
206     $this->drupalPostAjaxForm('ajax_forms_test_lazy_load_form', ['add_files' => TRUE], ['op' => t('Submit')]);
207
208     // Verify that the resulting HTML does not load the overridden CSS file.
209     // We add a "?" to the assertion, because drupalSettings may include
210     // information about the file; we only really care about whether it appears
211     // in a LINK or STYLE tag, for which Drupal always adds a query string for
212     // cache control.
213     $this->assertNoText('js.module.css?', 'Ajax lazy loading does not add overridden CSS files.');
214   }
215
216 }