JavaScriptTest.php 26 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php

/**
 * @file
 * Definition of Drupal\system\Tests\Common\JavaScriptTest.
 */

namespace Drupal\system\Tests\Common;

use Drupal\simpletest\WebTestBase;

/**
 * Tests the JavaScript system.
 */
class JavaScriptTest extends WebTestBase {
16 17 18 19 20 21 22 23

  /**
   * Enable Language and SimpleTest in the test environment.
   *
   * @var array
   */
  public static $modules = array('language', 'simpletest', 'common_test', 'path');

24
  /**
25
   * Stores configured value for JavaScript preprocessing.
26 27 28 29 30 31 32 33 34 35 36 37
   */
  protected $preprocess_js = NULL;

  public static function getInfo() {
    return array(
      'name' => 'JavaScript',
      'description' => 'Tests the JavaScript system.',
      'group' => 'Common',
    );
  }

  function setUp() {
38
    parent::setUp();
39 40 41

    // Disable preprocessing
    $config = config('system.performance');
42 43
    $this->preprocess_js = $config->get('js.preprocess');
    $config->set('js.preprocess', 0);
44 45 46 47 48 49 50 51 52 53
    $config->save();

    // Reset drupal_add_js() and drupal_add_library() statics before each test.
    drupal_static_reset('drupal_add_js');
    drupal_static_reset('drupal_add_library');
  }

  function tearDown() {
    // Restore configured value for JavaScript preprocessing.
    $config = config('system.performance');
54
    $config->set('js.preprocess', $this->preprocess_js);
55 56 57 58 59
    $config->save();
    parent::tearDown();
  }

  /**
60
   * Tests that default JavaScript is empty.
61 62
   */
  function testDefault() {
63
    $this->assertEqual(array(), drupal_add_js(), 'Default JavaScript is empty.');
64 65 66
  }

  /**
67
   * Tests adding a JavaScript file.
68 69 70
   */
  function testAddFile() {
    $javascript = drupal_add_js('core/misc/collapse.js');
71
    $this->assertTrue(array_key_exists('core/misc/collapse.js', $javascript), 'JavaScript files are correctly added.');
72 73 74
  }

  /**
75
   * Tests adding settings.
76 77
   */
  function testAddSetting() {
78
    // Add a file in order to test default settings.
79
    drupal_add_library('system', 'drupalSettings');
80 81
    $javascript = drupal_add_js();
    $last_settings = reset($javascript['settings']['data']);
82 83
    $this->assertTrue($last_settings['currentPath'], 'The current path JavaScript setting is set correctly.');

84
    $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
85 86 87
    $last_settings = end($javascript['settings']['data']);
    $this->assertEqual(280342800, $last_settings['dries'], 'JavaScript setting is set correctly.');
    $this->assertEqual('rocks', $last_settings['drupal'], 'The other JavaScript setting is set correctly.');
88 89 90 91 92 93 94 95
  }

  /**
   * Tests adding an external JavaScript File.
   */
  function testAddExternal() {
    $path = 'http://example.com/script.js';
    $javascript = drupal_add_js($path, 'external');
96
    $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), 'Added an external JavaScript file.');
97 98 99
  }

  /**
100
   * Tests adding JavaScript files with additional attributes.
101
   */
102
  function testAttributes() {
103 104
    $default_query_string = variable_get('css_js_query_string', '0');

105
    drupal_add_library('system', 'drupal');
106 107
    drupal_add_js('http://example.com/script.js', array('attributes' => array('defer' => 'defer')));
    drupal_add_js('core/misc/collapse.js', array('attributes' => array('defer' => 'defer')));
108 109
    $javascript = drupal_get_js();

110 111
    $expected_1 = '<script src="http://example.com/script.js?' . $default_query_string . '" defer="defer"></script>';
    $expected_2 = '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '" defer="defer"></script>';
112

113 114
    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute.');
    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute.');
115 116 117
  }

  /**
118
   * Tests that attributes are maintained when JS aggregation is enabled.
119
   */
120 121
  function testAggregatedAttributes() {
    // Enable aggregation.
122
    config('system.performance')->set('js.preprocess', 1)->save();
123

124 125
    $default_query_string = variable_get('css_js_query_string', '0');

126
    drupal_add_library('system', 'drupal');
127 128
    drupal_add_js('http://example.com/script.js', array('attributes' => array('defer' => 'defer')));
    drupal_add_js('core/misc/collapse.js', array('attributes' => array('defer' => 'defer')));
129 130
    $javascript = drupal_get_js();

131 132
    $expected_1 = '<script src="http://example.com/script.js?' . $default_query_string . '" defer="defer"></script>';
    $expected_2 = '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '" defer="defer"></script>';
133

134 135
    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute with aggregation enabled.');
    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute with aggregation enabled.');
136 137 138
  }

  /**
139
   * Tests drupal_get_js() for JavaScript settings.
140 141
   */
  function testHeaderSetting() {
142
    drupal_add_library('system', 'drupalSettings');
143 144 145 146 147 148 149

    $javascript = drupal_get_js('header');
    $this->assertTrue(strpos($javascript, 'basePath') > 0, 'Rendered JavaScript header returns basePath setting.');
    $this->assertTrue(strpos($javascript, 'scriptPath') > 0, 'Rendered JavaScript header returns scriptPath setting.');
    $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, 'Rendered JavaScript header returns pathPrefix setting.');
    $this->assertTrue(strpos($javascript, 'currentPath') > 0, 'Rendered JavaScript header returns currentPath setting.');

150 151 152
    // Only the second of these two entries should appear in Drupal.settings.
    drupal_add_js(array('commonTest' => 'commonTestShouldNotAppear'), 'setting');
    drupal_add_js(array('commonTest' => 'commonTestShouldAppear'), 'setting');
153 154 155
    // Only the second of these entries should appear in Drupal.settings.
    drupal_add_js(array('commonTestJsArrayLiteral' => array('commonTestJsArrayLiteralOldValue')), 'setting');
    drupal_add_js(array('commonTestJsArrayLiteral' => array('commonTestJsArrayLiteralNewValue')), 'setting');
156
    // Only the second of these two entries should appear in Drupal.settings.
157 158 159 160 161 162 163 164 165 166 167 168
    drupal_add_js(array('commonTestJsObjectLiteral' => array('key' => 'commonTestJsObjectLiteralOldValue')), 'setting');
    drupal_add_js(array('commonTestJsObjectLiteral' => array('key' => 'commonTestJsObjectLiteralNewValue')), 'setting');
    // Real world test case: multiple elements in a render array are adding the
    // same (or nearly the same) JavaScript settings. When merged, they should
    // contain all settings and not duplicate some settings.
    $settings_one = array('moduleName' => array('ui' => array('button A', 'button B'), 'magical flag' => 3.14159265359));
    drupal_add_js(array('commonTestRealWorldIdentical' => $settings_one), 'setting');
    drupal_add_js(array('commonTestRealWorldIdentical' => $settings_one), 'setting');
    $settings_two = array('moduleName' => array('ui' => array('button A', 'button B'), 'magical flag' => 3.14159265359, 'thingiesOnPage' => array('id1' => array())));
    drupal_add_js(array('commonTestRealWorldAlmostIdentical' => $settings_two), 'setting');
    $settings_two = array('moduleName' => array('ui' => array('button C', 'button D'), 'magical flag' => 3.14, 'thingiesOnPage' => array('id2' => array())));
    drupal_add_js(array('commonTestRealWorldAlmostIdentical' => $settings_two), 'setting');
169 170

    $javascript = drupal_get_js('header');
171

172
    // Test whether drupal_add_js can be used to override a previous setting.
173 174
    $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, 'Rendered JavaScript header returns custom setting.');
    $this->assertTrue(strpos($javascript, 'commonTestShouldNotAppear') === FALSE, 'drupal_add_js() correctly overrides a custom setting.');
175

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
    // Test whether drupal_add_js can be used to add and override a JavaScript
    // array literal (an indexed PHP array) values.
    $array_override = strpos($javascript, 'commonTestJsArrayLiteralNewValue') > 0 && strpos($javascript, 'commonTestJsArrayLiteralOldValue') === FALSE;
    $this->assertTrue($array_override, 'drupal_add_js() correctly overrides settings within an array literal (indexed array).');

    // Test whether drupal_add_js can be used to add and override a JavaScript
    // object literal (an associate PHP array) values.
    $associative_array_override = strpos($javascript, 'commonTestJsObjectLiteralNewValue') > 0 && strpos($javascript, 'commonTestJsObjectLiteralOldValue') === FALSE;
    $this->assertTrue($associative_array_override, 'drupal_add_js() correctly overrides settings within an object literal (associative array).');

    // Parse the generated drupalSettings <script> back to a PHP representation.
    $startToken = 'drupalSettings = ';
    $endToken = '}';
    $start = strpos($javascript, $startToken) + strlen($startToken);
    $end = strrpos($javascript, $endToken);
    $json  = drupal_substr($javascript, $start, $end - $start + 1);
    $parsed_settings = drupal_json_decode($json);

    // Test whether the two real world cases are handled correctly.
    $settings_two['moduleName']['thingiesOnPage']['id1'] = array();
    $this->assertIdentical($settings_one, $parsed_settings['commonTestRealWorldIdentical'], 'drupal_add_js handled real world test case 1 correctly.');
    $this->assertEqual($settings_two, $parsed_settings['commonTestRealWorldAlmostIdentical'], 'drupal_add_js handled real world test case 2 correctly.');
198

199 200
    // Check in a rendered page.
    $this->drupalGet('common-test/query-string');
201
    $this->assertPattern('@<script>.+drupalSettings.+"currentPath":"common-test\\\/query-string"@s', 'currentPath is in the JS settings');
202
    $path = array('source' => 'common-test/query-string', 'alias' => 'common-test/currentpath-check');
203
    drupal_container()->get('path.crud')->save($path['source'], $path['alias']);
204
    $this->drupalGet('common-test/currentpath-check');
205
    $this->assertPattern('@<script>.+drupalSettings.+"currentPath":"common-test\\\/query-string"@s', 'currentPath is in the JS settings for an aliased path');
206 207 208
  }

  /**
209
   * Tests to see if resetting the JavaScript empties the cache.
210 211
   */
  function testReset() {
212
    drupal_add_library('system', 'drupal');
213 214
    drupal_add_js('core/misc/collapse.js');
    drupal_static_reset('drupal_add_js');
215
    $this->assertEqual(array(), drupal_add_js(), 'Resetting the JavaScript correctly empties the cache.');
216 217 218
  }

  /**
219
   * Tests adding inline scripts.
220 221
   */
  function testAddInline() {
222
    drupal_add_library('system', 'drupal');
223 224
    $inline = 'jQuery(function () { });';
    $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
225
    $this->assertTrue(array_key_exists('core/misc/jquery.js', $javascript), 'jQuery is added when inline scripts are added.');
226
    $data = end($javascript);
227
    $this->assertEqual($inline, $data['data'], 'Inline JavaScript is correctly added to the footer.');
228 229 230
  }

  /**
231
   * Tests rendering an external JavaScript file.
232 233
   */
  function testRenderExternal() {
234
    drupal_add_library('system', 'drupal');
235 236 237 238
    $external = 'http://example.com/example.js';
    drupal_add_js($external, 'external');
    $javascript = drupal_get_js();
    // Local files have a base_path() prefix, external files should not.
239
    $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, 'Rendering an external JavaScript file.');
240 241 242
  }

  /**
243
   * Tests drupal_get_js() with a footer scope.
244 245
   */
  function testFooterHTML() {
246
    drupal_add_library('system', 'drupal');
247 248 249
    $inline = 'jQuery(function () { });';
    drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
    $javascript = drupal_get_js('footer');
250
    $this->assertTrue(strpos($javascript, $inline) > 0, 'Rendered JavaScript footer returns the inline code.');
251 252 253
  }

  /**
254
   * Tests drupal_add_js() sets preproccess to FALSE when cache is also FALSE.
255 256
   */
  function testNoCache() {
257
    drupal_add_library('system', 'drupal');
258
    $javascript = drupal_add_js('core/misc/collapse.js', array('cache' => FALSE));
259
    $this->assertFalse($javascript['core/misc/collapse.js']['preprocess'], 'Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.');
260 261 262
  }

  /**
263
   * Tests adding a JavaScript file with a different group.
264 265
   */
  function testDifferentGroup() {
266
    drupal_add_library('system', 'drupal');
267
    $javascript = drupal_add_js('core/misc/collapse.js', array('group' => JS_THEME));
268
    $this->assertEqual($javascript['core/misc/collapse.js']['group'], JS_THEME, 'Adding a JavaScript file with a different group caches the given group.');
269 270 271
  }

  /**
272
   * Tests adding a JavaScript file with a different weight.
273 274 275
   */
  function testDifferentWeight() {
    $javascript = drupal_add_js('core/misc/collapse.js', array('weight' => 2));
276
    $this->assertEqual($javascript['core/misc/collapse.js']['weight'], 2, 'Adding a JavaScript file with a different weight caches the given weight.');
277 278 279
  }

  /**
280
   * Tests adding JavaScript within conditional comments.
281 282 283 284 285 286
   *
   * @see drupal_pre_render_conditional_comments()
   */
  function testBrowserConditionalComments() {
    $default_query_string = variable_get('css_js_query_string', '0');

287
    drupal_add_library('system', 'drupal');
288 289 290 291
    drupal_add_js('core/misc/collapse.js', array('browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE)));
    drupal_add_js('jQuery(function () { });', array('type' => 'inline', 'browsers' => array('IE' => FALSE)));
    $javascript = drupal_get_js();

292 293
    $expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
    $expected_2 = "<!--[if !IE]><!-->\n" . '<script>' . "\n<!--//--><![CDATA[//><!--\n" . 'jQuery(function () { });' . "\n//--><!]]>\n" . '</script>' . "\n<!--<![endif]-->";
294

295 296
    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered JavaScript within downlevel-hidden conditional comments.');
    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered JavaScript within downlevel-revealed conditional comments.');
297 298 299
  }

  /**
300
   * Tests JavaScript versioning.
301 302
   */
  function testVersionQueryString() {
303
    drupal_add_library('system', 'drupal');
304 305 306
    drupal_add_js('core/misc/collapse.js', array('version' => 'foo'));
    drupal_add_js('core/misc/ajax.js', array('version' => 'bar'));
    $javascript = drupal_get_js();
307
    $this->assertTrue(strpos($javascript, 'core/misc/collapse.js?v=foo') > 0 && strpos($javascript, 'core/misc/ajax.js?v=bar') > 0 , 'JavaScript version identifiers correctly appended to URLs');
308 309 310
  }

  /**
311
   * Tests JavaScript grouping and aggregation.
312 313 314 315 316 317 318 319
   */
  function testAggregation() {
    $default_query_string = variable_get('css_js_query_string', '0');

    // To optimize aggregation, items with the 'every_page' option are ordered
    // ahead of ones without. The order of JavaScript execution must be the
    // same regardless of whether aggregation is enabled, so ensure this
    // expected order, first with aggregation off.
320
    drupal_add_library('system', 'drupal');
321 322 323 324 325 326
    drupal_add_js('core/misc/ajax.js');
    drupal_add_js('core/misc/collapse.js', array('every_page' => TRUE));
    drupal_add_js('core/misc/autocomplete.js');
    drupal_add_js('core/misc/batch.js', array('every_page' => TRUE));
    $javascript = drupal_get_js();
    $expected = implode("\n", array(
327 328 329 330
      '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>',
      '<script src="' . file_create_url('core/misc/batch.js') . '?' . $default_query_string . '"></script>',
      '<script src="' . file_create_url('core/misc/ajax.js') . '?' . $default_query_string . '"></script>',
      '<script src="' . file_create_url('core/misc/autocomplete.js') . '?' . $default_query_string . '"></script>',
331
    ));
332
    $this->assertTrue(strpos($javascript, $expected) > 0, 'Unaggregated JavaScript is added in the expected group order.');
333 334 335 336 337

    // Now ensure that with aggregation on, one file is made for the
    // 'every_page' files, and one file is made for the others.
    drupal_static_reset('drupal_add_js');
    $config = config('system.performance');
338
    $config->set('js.preprocess', 1);
339
    $config->save();
340
    drupal_add_library('system', 'drupal');
341 342 343 344 345 346 347
    drupal_add_js('core/misc/ajax.js');
    drupal_add_js('core/misc/collapse.js', array('every_page' => TRUE));
    drupal_add_js('core/misc/autocomplete.js');
    drupal_add_js('core/misc/batch.js', array('every_page' => TRUE));
    $js_items = drupal_add_js();
    $javascript = drupal_get_js();
    $expected = implode("\n", array(
348 349
      '<script src="' . file_create_url(drupal_build_js_cache(array('core/misc/collapse.js' => $js_items['core/misc/collapse.js'], 'core/misc/batch.js' => $js_items['core/misc/batch.js']))) . '"></script>',
      '<script src="' . file_create_url(drupal_build_js_cache(array('core/misc/ajax.js' => $js_items['core/misc/ajax.js'], 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']))) . '"></script>',
350
    ));
351
    $this->assertTrue(strpos($javascript, $expected) !== FALSE, 'JavaScript is aggregated in the expected groups and order.');
352 353 354 355 356 357 358
  }

  /**
   * Tests JavaScript aggregation when files are added to a different scope.
   */
  function testAggregationOrder() {
    // Enable JavaScript aggregation.
359
    config('system.performance')->set('js.preprocess', 1)->save();
360 361 362
    drupal_static_reset('drupal_add_js');

    // Add two JavaScript files to the current request and build the cache.
363
    drupal_add_library('system', 'drupal');
364 365 366 367 368 369 370 371 372 373
    drupal_add_js('core/misc/ajax.js');
    drupal_add_js('core/misc/autocomplete.js');

    $js_items = drupal_add_js();
    drupal_build_js_cache(array(
      'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
      'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
    ));

    // Store the expected key for the first item in the cache.
374
    $cache = array_keys(state()->get('system.js_cache_files') ?: array());
375 376 377
    $expected_key = $cache[0];

    // Reset variables and add a file in a different scope first.
378
    state()->delete('system.js_cache_files');
379
    drupal_static_reset('drupal_add_js');
380
    drupal_add_library('system', 'drupal');
381 382 383 384 385 386 387 388 389 390 391 392
    drupal_add_js('some/custom/javascript_file.js', array('scope' => 'footer'));
    drupal_add_js('core/misc/ajax.js');
    drupal_add_js('core/misc/autocomplete.js');

    // Rebuild the cache.
    $js_items = drupal_add_js();
    drupal_build_js_cache(array(
      'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
      'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
    ));

    // Compare the expected key for the first file to the current one.
393
    $cache = array_keys(state()->get('system.js_cache_files') ?: array());
394 395 396 397 398
    $key = $cache[0];
    $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.');
  }

  /**
399
   * Tests JavaScript ordering.
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
   */
  function testRenderOrder() {
    // Add a bunch of JavaScript in strange ordering.
    drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
    drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
    drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
    drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
    drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
    drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
    drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5));
    drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
    drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
    drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));

    // Construct the expected result from the regex.
    $expected = array(
      "-8 #1",
      "-8 #2",
      "-8 #3",
      "-8 #4",
      "-5 #1", // The external script.
      "0 #1",
      "0 #2",
      "0 #3",
      "5 #1",
      "5 #2",
    );

    // Retrieve the rendered JavaScript and test against the regex.
    $js = drupal_get_js('footer');
    $matches = array();
    if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
      $result = $matches[1];
    }
    else {
      $result = array();
    }
437
    $this->assertIdentical($result, $expected, 'JavaScript is added in the expected weight order.');
438 439 440
  }

  /**
441
   * Tests rendering the JavaScript with a file's weight above jQuery's.
442 443 444 445 446
   */
  function testRenderDifferentWeight() {
    // JavaScript files are sorted first by group, then by the 'every_page'
    // flag, then by weight (see drupal_sort_css_js()), so to test the effect of
    // weight, we need the other two options to be the same.
447
    drupal_add_library('system', 'jquery');
448 449
    drupal_add_js('core/misc/collapse.js', array('group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -21));
    $javascript = drupal_get_js();
450
    $this->assertTrue(strpos($javascript, 'core/misc/collapse.js') < strpos($javascript, 'core/misc/jquery.js'), 'Rendering a JavaScript file above jQuery.');
451 452 453
  }

  /**
454
   * Tests altering a JavaScript's weight via hook_js_alter().
455 456 457 458 459 460 461 462 463 464 465 466
   *
   * @see simpletest_js_alter()
   */
  function testAlter() {
    // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
    drupal_add_js('core/misc/tableselect.js');
    drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => 9999));

    // Render the JavaScript, testing if simpletest.js was altered to be before
    // tableselect.js. See simpletest_js_alter() to see where this alteration
    // takes place.
    $javascript = drupal_get_js();
467
    $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
468 469 470 471 472 473
  }

  /**
   * Adds a library to the page and tests for both its JavaScript and its CSS.
   */
  function testLibraryRender() {
474
    $result = drupal_add_library('system', 'jquery.farbtastic');
475
    $this->assertTrue($result !== FALSE, 'Library was added without errors.');
476 477
    $scripts = drupal_get_js();
    $styles = drupal_get_css();
478 479
    $this->assertTrue(strpos($scripts, 'core/misc/farbtastic/farbtastic.js'), 'JavaScript of library was added to the page.');
    $this->assertTrue(strpos($styles, 'core/misc/farbtastic/farbtastic.css'), 'Stylesheet of library was added to the page.');
480 481 482 483 484 485 486 487 488
  }

  /**
   * Adds a JavaScript library to the page and alters it.
   *
   * @see common_test_library_info_alter()
   */
  function testLibraryAlter() {
    // Verify that common_test altered the title of Farbtastic.
489
    $library = drupal_get_library('system', 'jquery.farbtastic');
490
    $this->assertEqual($library['title'], 'Farbtastic: Altered Library', 'Registered libraries were altered.');
491 492

    // common_test_library_info_alter() also added a dependency on jQuery Form.
493
    drupal_add_library('system', 'jquery.farbtastic');
494
    $scripts = drupal_get_js();
495
    $this->assertTrue(strpos($scripts, 'core/misc/jquery.form.js'), 'Altered library dependencies are added to the page.');
496 497 498 499 500 501 502 503
  }

  /**
   * Tests that multiple modules can implement the same library.
   *
   * @see common_test_library_info()
   */
  function testLibraryNameConflicts() {
504
    $farbtastic = drupal_get_library('common_test', 'jquery.farbtastic');
505
    $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', 'Alternative libraries can be added to the page.');
506 507 508 509 510 511 512
  }

  /**
   * Tests non-existing libraries.
   */
  function testLibraryUnknown() {
    $result = drupal_get_library('unknown', 'unknown');
513
    $this->assertFalse($result, 'Unknown library returned FALSE.');
514 515 516
    drupal_static_reset('drupal_get_library');

    $result = drupal_add_library('unknown', 'unknown');
517
    $this->assertFalse($result, 'Unknown library returned FALSE.');
518
    $scripts = drupal_get_js();
519
    $this->assertTrue(strpos($scripts, 'unknown') === FALSE, 'Unknown library was not added to the page.');
520 521 522 523 524 525
  }

  /**
   * Tests the addition of libraries through the #attached['library'] property.
   */
  function testAttachedLibrary() {
526
    $element['#attached']['library'][] = array('system', 'jquery.farbtastic');
527 528
    drupal_render($element);
    $scripts = drupal_get_js();
529
    $this->assertTrue(strpos($scripts, 'core/misc/farbtastic/farbtastic.js'), 'The attached_library property adds the additional libraries.');
530 531 532 533 534 535 536 537
  }

  /**
   * Tests retrieval of libraries via drupal_get_library().
   */
  function testGetLibrary() {
    // Retrieve all libraries registered by a module.
    $libraries = drupal_get_library('common_test');
538
    $this->assertTrue(isset($libraries['jquery.farbtastic']), 'Retrieved all module libraries.');
539
    // Retrieve all libraries for a module not implementing hook_library_info().
540 541
    // Note: This test installs language module.
    $libraries = drupal_get_library('language');
542
    $this->assertEqual($libraries, array(), 'Retrieving libraries from a module not implementing hook_library_info() returns an emtpy array.');
543 544

    // Retrieve a specific library by module and name.
545
    $farbtastic = drupal_get_library('common_test', 'jquery.farbtastic');
546
    $this->assertEqual($farbtastic['version'], '5.3', 'Retrieved a single library.');
547 548
    // Retrieve a non-existing library by module and name.
    $farbtastic = drupal_get_library('common_test', 'foo');
549
    $this->assertIdentical($farbtastic, FALSE, 'Retrieving a non-existing library returns FALSE.');
550 551 552
  }

  /**
553
   * Tests JavaScript files that have querystrings attached get added right.
554 555 556 557
   */
  function testAddJsFileWithQueryString() {
    $this->drupalGet('common-test/query-string');
    $query_string = variable_get('css_js_query_string', '0');
558
    $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, 'Query string was appended correctly to js.');
559 560
  }
}