Tests for advagg.module.

File

tests/advagg.test

View source
<?php


/**
 * @file
 * Tests for advagg.module.
 */

/**
 * @defgroup advagg_tests Advanced Aggregates Tests
 *
 * @{
 * Advanced Aggregates testing functionality.
 */
// Include all files in the project for simple sanity checking.
$files = file_scan_directory(drupal_get_path('module', 'advagg'), "/.*\\.(inc|module|install|php)\$/", array(
    'nomask' => '/(\\.\\.?|CVS|tpl\\.php)$/',
));
foreach ($files as $file) {
    include_once DRUPAL_ROOT . '/' . $file->uri;
}

/**
 * Resets static variables related to adding CSS or JS to a page.
 */
function advagg_test_reset_statics() {
    drupal_static_reset('drupal_add_css');
    drupal_static_reset('drupal_add_library');
    drupal_static_reset('drupal_get_library');
    drupal_static_reset('drupal_add_js');
    drupal_static_reset('drupal_add_js:jquery_added');
    drupal_static_reset('advagg_get_js');
}

/**
 * Generates a large CSS string.
 *
 * @param int $selector_count
 *   The number of selectors to generate.
 * @param int $denominator
 *   The max string length of the selector names.
 *
 * @return string
 *   Generated CSS string.
 */
function advagg_test_generate_selector_css($selector_count, $denominator = 5) {
    static $count = 0;
    $pool = array_merge(range('a', 'z'), range('A', 'Z'));
    $selector_count = 10000;
    $css = '';
    while ($selector_count > 0) {
        $rand_string = advagg_test_randon_string($selector_count % $denominator + 3, $pool);
        $css .= ".{$rand_string}, ";
        --$selector_count;
    }
    $css .= "#last{$count} {z-index: 2; margin-left: -1px; content: \" \"; display: table;}";
    ++$count;
    return $css;
}

/**
 * Generates random string.
 *
 * @param int $length
 *   How many characters will this string contain.
 * @param array $pool
 *   Array of characters to use.
 *
 * @return string
 *   Random string.
 */
function advagg_test_randon_string($length, array $pool) {
    $string = '';
    $count = count($pool);
    for ($i = 0; $i < $length; $i++) {
        $string .= $pool[mt_rand(0, $count - 1)];
    }
    return $string;
}

/**
 * Strip the codingStandardsIgnoreFile string from the input.
 *
 * @param string $input
 *   The input string.
 *
 * @return string
 *   The input string with codingStandardsIgnoreFile removed from it.
 */
function advagg_test_remove_sniffer_comments($input) {
    $string = "/* @codingStandardsIgnoreFile */\n";
    return str_replace($string, '', $input);
}

/**
 * Test the Drupal CSS system.
 */
class AdvAggCascadingStylesheetsTestCase extends DrupalWebTestCase {
    
    /**
     * Store configured value for CSS preprocessing.
     *
     * @var bool
     */
    protected $preprocessCss = NULL;
    
    /**
     * Create user.
     *
     * @var object
     */
    protected $bigUser;
    
    /**
     * Theme settings.
     *
     * @var array
     */
    protected $themes;
    
    /**
     * Provide information to the UI for this test.
     */
    public static function getInfo() {
        return array(
            'name' => 'CSS',
            'description' => 'Tests adding various cascading stylesheets to the page.',
            'group' => 'AdvAgg',
        );
    }
    
    /**
     * Install the advagg module and include needed files.
     */
    public function setUp() {
        // Enable any modules required for the test. This should be an array of
        // module names.
        parent::setUp(array(
            'advagg',
            'php',
            'locale',
            'common_test',
            'menu_test',
            'color',
        ));
        // Include the advagg.module file.
        drupal_load('module', 'advagg');
        module_load_include('inc', 'advagg', 'advagg');
        // Set settings for testing.
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
        $GLOBALS['conf']['advagg_skip_file_create_url_inside_css'] = FALSE;
        // Disable CSS preprocessing.
        $this->preprocessCss = variable_get('preprocess_css', 0);
        variable_set('preprocess_css', 0);
        // Create users.
        $this->bigUser = $this->drupalCreateUser(array(
            'administer themes',
        ));
        // This tests the color module in both Bartik and Garland.
        $this->themes = array(
            'bartik' => array(
                'palette_input' => 'palette[bg]',
                'scheme' => 'slate',
                'scheme_color' => '#3b3b3b',
            ),
        );
        theme_enable(array_keys($this->themes));
        // Reset drupal_add_css() before each test.
        advagg_test_reset_statics();
    }
    
    /**
     * Restore any variables we set.
     */
    public function tearDown() {
        // Restore configured value for CSS preprocessing.
        variable_set('preprocess_css', $this->preprocessCss);
        parent::tearDown();
    }
    
    /**
     * Tests rendering the stylesheets.
     */
    public function testRenderFile() {
        foreach ($this->themes as $theme => $test_values) {
            variable_set('theme_default', $theme);
            $settings_path = 'admin/appearance/settings/' . $theme;
            $this->drupalLogin($this->bigUser);
            $this->drupalGet($settings_path);
            $this->assertResponse(200);
            $edit['scheme'] = '';
            $edit[$test_values['palette_input']] = '#123456';
            $this->drupalPost($settings_path, $edit, t('Save configuration'));
            // Reset drupal_add_css() before each test.
            $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
            $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
            advagg_test_reset_statics();
            // Add the css file.
            $stylesheets = variable_get('color_' . $theme . '_stylesheets', array());
            drupal_add_css($stylesheets[0]);
            $css = file_create_url($stylesheets[0]);
            // Get the render array.
            $full_css = advagg_get_css();
            $styles = drupal_render($full_css);
            $this->assertTrue(strpos($styles, $css) !== FALSE, "Rendered CSS includes the added stylesheet ({$css}).");
        }
        // Reset drupal_add_css() before each test.
        advagg_test_reset_statics();
        // Add the css file.
        $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
        drupal_add_css($css);
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        $this->assertTrue(strpos($styles, $css) > 0, "Rendered CSS includes the added stylesheet ({$css}).");
        // Verify that newlines are properly added inside style tags.
        $query_string = variable_get('css_js_query_string', '0');
        $css_processed = "<style type=\"text/css\" media=\"all\">\n@import url(\"" . check_plain(file_create_url($css)) . "?" . $query_string . "\");\n</style>";
        $this->assertEqual(trim($styles), $css_processed, 'Rendered CSS includes newlines inside style tags for JavaScript use.');
        //
        // Tests rendering an external stylesheet.
        advagg_test_reset_statics();
        // Add the css file.
        $css = 'http://example.com/style.css';
        drupal_add_css($css, 'external');
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        // Stylesheet URL may be the href of a LINK tag or in an @import statement
        // of a STYLE tag.
        $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, 'Rendering an external CSS file.');
        //
        // Tests rendering inline stylesheets with preprocessing on.
        advagg_test_reset_statics();
        // Add the inline css.
        $css = 'body { padding: 0px; }';
        list($embed_prefix, $embed_suffix) = advagg_get_css_prefix_suffix();
        $css_preprocessed = '<style type="text/css" media="all">' . $embed_prefix . advagg_load_stylesheet_content($css, TRUE) . $embed_suffix . '</style>';
        drupal_add_css($css, array(
            'type' => 'inline',
        ));
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        $this->assertEqual(trim($styles), $css_preprocessed, 'Rendering preprocessed inline CSS adds it to the page.');
        //
        // Tests removing charset when rendering stylesheets with preprocessing on.
        advagg_test_reset_statics();
        $cases = array(
            array(
                'asset' => '@charset "UTF-8";html{font-family:"sans-serif";}',
                'expected' => 'html{font-family:"sans-serif";}',
            ),
            // This asset contains extra \n character.
array(
                'asset' => "@charset 'UTF-8';\nhtml{font-family:'sans-serif';}",
                'expected' => "\nhtml{font-family:'sans-serif';}",
            ),
        );
        foreach ($cases as $case) {
            $this->assertEqual($case['expected'], advagg_load_stylesheet_content($case['asset']), 'CSS optimizing correctly removes the charset declaration.');
        }
        //
        // Tests rendering inline stylesheets with preprocessing off.
        advagg_test_reset_statics();
        // Add the inline css.
        $css = 'body { padding: 0px; }';
        drupal_add_css($css, array(
            'type' => 'inline',
            'preprocess' => FALSE,
        ));
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        $this->assertTrue(strpos($styles, $css) > 0, 'Rendering non-preprocessed inline CSS adds it to the page.');
        //
        // Test CSS ordering.
        advagg_test_reset_statics();
        // A module CSS file.
        drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
        // A few system CSS files, ordered in a strange way.
        $system_path = drupal_get_path('module', 'system');
        drupal_add_css($system_path . '/system.menus.css', array(
            'group' => CSS_SYSTEM,
        ));
        drupal_add_css($system_path . '/system.base.css', array(
            'group' => CSS_SYSTEM,
            'weight' => -10,
        ));
        drupal_add_css($system_path . '/system.theme.css', array(
            'group' => CSS_SYSTEM,
        ));
        $expected = array(
            $system_path . '/system.base.css',
            $system_path . '/system.menus.css',
            $system_path . '/system.theme.css',
            drupal_get_path('module', 'simpletest') . '/simpletest.css',
        );
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        // Stylesheet URL may be the href of a LINK tag or in an @import statement
        // of a STYLE tag.
        if (preg_match_all('/(href="|url\\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\\?/', $styles, $matches)) {
            $result = $matches[2];
        }
        else {
            $result = array();
        }
        $this->assertIdentical($result, $expected, 'The CSS files are in the expected order.');
        //
        // Test CSS override.
        advagg_test_reset_statics();
        $system = drupal_get_path('module', 'system');
        $simpletest = drupal_get_path('module', 'simpletest');
        drupal_add_css($system . '/system.base.css');
        drupal_add_css($simpletest . '/tests/system.base.css');
        // The dummy stylesheet should be the only one included.
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') !== FALSE, 'The overriding CSS file is output.');
        $this->assert(strpos($styles, $system . '/system.base.css') === FALSE, 'The overridden CSS file is not output.');
        // The reset is needed here until this is fixed
        // https://www.drupal.org/node/1388546
        advagg_test_reset_statics();
        drupal_add_css($simpletest . '/tests/system.base.css');
        drupal_add_css($system . '/system.base.css');
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        // The standard stylesheet should be the only one included.
        $this->assert(strpos($styles, $system . '/system.base.css') !== FALSE, 'The overriding CSS file is output.');
        $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') === FALSE, 'The overridden CSS file is not output.');
        //
        //  Tests Locale module's CSS Alter to include RTL overrides.
        advagg_test_reset_statics();
        // Switch the language to a right to left language and add system.base.css.
        global $language;
        $language->direction = LANGUAGE_RTL;
        $path = drupal_get_path('module', 'system');
        drupal_add_css($path . '/system.base.css', array(
            'group' => CSS_SYSTEM,
        ));
        drupal_add_css($path . '/system.menus.css', array(
            'group' => CSS_SYSTEM,
        ));
        drupal_add_css($path . '/system.theme.css', array(
            'group' => CSS_SYSTEM,
        ));
        // Get the render array.
        $full_css = advagg_get_css();
        // Render the CSS.
        $styles = drupal_render($full_css);
        // Check to see if system.base-rtl.css was also added.
        $base_pos = strpos($styles, $path . '/system.base.css');
        $base_rtl_pos = strpos($styles, $path . '/system.base-rtl.css');
        $this->assert($base_rtl_pos !== FALSE, 'CSS is alterable as right to left overrides are added.');
        $this->assert($base_pos < $base_rtl_pos, 'system.base-rtl.css is added after system.base.css.');
        // Check to see if system.menus-rtl.css was also added.
        $menus_pos = strpos($styles, $path . '/system.menus.css');
        $menus_rtl_pos = strpos($styles, $path . '/system.menus-rtl.css');
        $this->assert($menus_rtl_pos !== FALSE, 'CSS is alterable as right to left overrides are added.');
        $this->assert($menus_pos < $menus_rtl_pos, 'system.menus-rtl.css is added after system.menus.css.');
        // Check to see if system.theme-rtl.css was also added.
        $theme_pos = strpos($styles, $path . '/system.theme.css');
        $theme_rtl_pos = strpos($styles, $path . '/system.theme-rtl.css');
        $this->assert($theme_rtl_pos !== FALSE, 'CSS is alterable as right to left overrides are added.');
        $this->assert($theme_pos < $theme_rtl_pos, 'system.theme-rtl.css is added after system.theme.css.');
        // Change the language back to left to right.
        $language->direction = LANGUAGE_LTR;
        //
        // Tests rendering inline stylesheets through a full page request.
        advagg_test_reset_statics();
        $css = 'body { font-size: 254px; }';
        // Inline CSS is minified unless 'preprocess' => FALSE is passed as a
        // drupal_add_css() option.
        $expected = 'body{font-size:254px;}';
        // Create a node, using the PHP filter that tests drupal_add_css().
        $php_format_id = 'php_code';
        $settings = array(
            'type' => 'page',
            'body' => array(
                LANGUAGE_NONE => array(
                    array(
                        'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('{$css}', 'inline'); ?>",
                        'format' => $php_format_id,
                    ),
                ),
            ),
            'promote' => 1,
        );
        $node = $this->drupalCreateNode($settings);
        // Fetch the page.
        $this->drupalGet('node/' . $node->nid);
        $this->assertRaw($expected, 'Inline stylesheets appear in the full page rendering.');
        //
        // Tests that the query string remains intact when adding CSS files that
        // have query string parameters.
        advagg_test_reset_statics();
        $this->drupalGet('common-test/query-string');
        $query_string = variable_get('css_js_query_string', '0');
        $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, 'Query string was appended correctly to css.');
        $this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&amp;arg2=value2', 'Query string not escaped on a URI.');
        //
        // Make the tests below more robust by explicitly setting the default theme
        // and administrative theme that they expect.
        theme_enable(array(
            'bartik',
        ));
        variable_set('theme_default', 'bartik');
        variable_set('admin_theme', 'seven');
        // Test the theme callback when it is set to use an administrative theme.
        advagg_test_reset_statics();
        $this->drupalGet('menu-test/theme-callback/use-admin-theme');
        $this->assertText('Custom theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme callback.');
        $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
        //
        // Test that the theme callback is properly inherited.
        advagg_test_reset_statics();
        $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance');
        $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', 'Theme callback inheritance correctly uses the administrative theme.');
        $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
        //
        // Test the theme callback when the site is in maintenance mode.
        advagg_test_reset_statics();
        variable_set('maintenance_mode', TRUE);
        // For a regular user, the fact that the site is in maintenance mode means
        // we expect the theme callback system to be bypassed entirely.
        $this->drupalGet('menu-test/theme-callback/use-admin-theme');
        $this->assertRaw('bartik/css/style.css', "The maintenance theme's CSS appears on the page.");
        // An administrator, however, should continue to see the requested theme.
        $admin_user = $this->drupalCreateUser(array(
            'access site in maintenance mode',
        ));
        $this->drupalLogin($admin_user);
        $this->drupalGet('menu-test/theme-callback/use-admin-theme');
        $this->assertText('Custom theme: seven. Actual theme: seven.', 'The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.');
        $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
        variable_set('maintenance_mode', FALSE);
        //
        // Test the theme callback when it is set to use an optional theme.
        advagg_test_reset_statics();
        // Request a theme that is not enabled.
        $this->drupalGet('menu-test/theme-callback/use-stark-theme');
        $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that is not enabled is requested.');
        $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
        // Now enable the theme and request it again.
        theme_enable(array(
            'stark',
        ));
        $this->drupalGet('menu-test/theme-callback/use-stark-theme');
        $this->assertText('Custom theme: stark. Actual theme: stark.', 'The theme callback system uses an optional theme once it has been enabled.');
        $this->assertRaw('stark/layout.css', "The optional theme's CSS appears on the page.");
        // Test the theme callback when it is set to use a theme that does not
        // exist.
        $this->drupalGet('menu-test/theme-callback/use-fake-theme');
        $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that does not exist is requested.');
        $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
        //
        // Test the theme callback when no theme is requested.
        advagg_test_reset_statics();
        $this->drupalGet('menu-test/theme-callback/no-theme-requested');
        $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when no theme is requested.');
        $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
        //
        // Test that hook_custom_theme() can control the theme of a page.
        advagg_test_reset_statics();
        // Trigger hook_custom_theme() to dynamically request the Stark theme for
        // the requested page.
        variable_set('menu_test_hook_custom_theme_name', 'stark');
        theme_enable(array(
            'stark',
        ));
        // Visit a page that does not implement a theme callback. The above request
        // should be honored.
        $this->drupalGet('menu-test/no-theme-callback');
        $this->assertText('Custom theme: stark. Actual theme: stark.', 'The result of hook_custom_theme() is used as the theme for the current page.');
        $this->assertRaw('stark/layout.css', "The Stark theme's CSS appears on the page.");
        //
        // Test that the theme callback wins out over hook_custom_theme().
        advagg_test_reset_statics();
        // Trigger hook_custom_theme() to dynamically request the Stark theme for
        // the requested page.
        variable_set('menu_test_hook_custom_theme_name', 'stark');
        theme_enable(array(
            'stark',
        ));
        // The menu "theme callback" should take precedence over a value set in
        // hook_custom_theme().
        $this->drupalGet('menu-test/theme-callback/use-admin-theme');
        $this->assertText('Custom theme: seven. Actual theme: seven.', 'The result of hook_custom_theme() does not override what was set in a theme callback.');
        $this->assertRaw('seven/style.css', "The Seven theme's CSS appears on the page.");
        //
        // Test css split file processing.
        // Generate a massive css file.
        $css_string = advagg_test_generate_selector_css(1000);
        $css_string .= '@media print {' . advagg_test_generate_selector_css(1000) . '}';
        $css_string .= advagg_test_generate_selector_css(1000);
        $css_string .= '@media screen {' . advagg_test_generate_selector_css(1000) . '}';
        $css_string .= advagg_test_generate_selector_css(1000);
        $css_string .= '@media print {' . advagg_test_generate_selector_css(1000) . '}';
        $css_string .= advagg_test_generate_selector_css(9000);
        $css_string .= '@media print {' . advagg_test_generate_selector_css(9000) . '}';
        $css_string .= advagg_test_generate_selector_css(9000);
        $css_string .= '@media screen {' . advagg_test_generate_selector_css(9000) . '}';
        $css_string .= '@media print {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= '@media screen {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= '@media print {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= '@media screen {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= '@media print {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= '@media screen {' . advagg_test_generate_selector_css(50) . '}';
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(15000);
        $css_string .= '@media print {' . advagg_test_generate_selector_css(15000) . '}';
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $css_string .= advagg_test_generate_selector_css(10);
        $file_info = array(
            // Use a file that exists but isn't actually being used here.
'data' => drupal_get_path('module', 'advagg') . '/tests/css_test_files/advagg.css',
        );
        $before_selector_count = advagg_count_css_selectors($css_string);
        // Split the huge css file.
        $parts = advagg_split_css_file($file_info, $css_string);
        $after = '';
        foreach ($parts as $part) {
            // Get written file.
            $after .= "\n" . (string) @advagg_file_get_contents($part['data']);
            // Cleanup.
            unlink($part['data']);
        }
        // Note that a diff of the text can not be used for this test. Counting
        // selectors is close enough for now.
        $after_selector_count = advagg_count_css_selectors($after);
        $this->assertEqual($before_selector_count, $after_selector_count, t('Expected %before selectors, got %after.', array(
            '%before' => $before_selector_count,
            '%after' => $after_selector_count,
        )));
        //
        // Test css file processing.
        advagg_test_reset_statics();
        // Array of files to test living in 'advagg/tests/css_test_files/'.
        // - Original: name.css
        // - Unoptimized expected content: name.css.unoptimized.css
        // - Optimized expected content: name.css.optimized.css
        //
        // File. Tests: css_input_without_import.css.
        // - Stripped comments and white-space.
        // - Retain white-space in selectors. (http://drupal.org/node/472820)
        // - Retain pseudo-selectors. (http://drupal.org/node/460448)
        //
        // File. Tests: css_input_with_import.css.
        // - Proper URLs in imported files. (http://drupal.org/node/265719)
        // - A background image with relative paths, which must be rewritten.
        // - The rewritten background image path must also be passed through
        //   file_create_url(). (https://drupal.org/node/1961340)
        // - Imported files that are external (protocol-relative URL or not)
        //   should not be expanded. (https://drupal.org/node/2014851)
        //
        // File in sub-folder. Tests: css_subfolder/css_input_with_import.css.
        // - CSS import path interpreted. (https://drupal.org/node/1198904)
        // - Don't adjust data URIs (https://drupal.org/node/2142441)
        //
        // File. Tests: comment_hacks.css.
        // - Retain comment hacks.
        $testfiles = array(
            'css_input_without_import.css',
            'css_input_with_import.css',
            'css_subfolder/css_input_with_import.css',
            'comment_hacks.css',
        );
        $path = drupal_get_path('module', 'advagg') . '/tests/css_test_files';
        foreach ($testfiles as $file) {
            $file_path = $path . '/' . $file;
            $file_url = $GLOBALS['base_url'] . '/' . $file_path;
            $expected = advagg_file_get_contents($file_path . '.unoptimized.css');
            $unoptimized_output = advagg_load_stylesheet($file_path, FALSE);
            $this->assertEqual($unoptimized_output, $expected, format_string('Unoptimized CSS file has expected contents (@file)', array(
                '@file' => $file,
            )));
            $expected = advagg_file_get_contents($file_path . '.optimized.css');
            $expected = advagg_test_remove_sniffer_comments($expected);
            $optimized_output = advagg_load_stylesheet($file_path, TRUE);
            $this->assertEqual($optimized_output, $expected, format_string('Optimized CSS file has expected contents (@file)', array(
                '@file' => $file,
            )));
            // Repeat the tests by accessing the stylesheets by URL.
            $expected = advagg_file_get_contents($file_path . '.unoptimized.css');
            $unoptimized_output_url = advagg_load_stylesheet($file_url, FALSE);
            $this->assertEqual($unoptimized_output_url, $expected, format_string('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array(
                '@file' => $file,
            )));
            $expected = advagg_file_get_contents($file_path . '.optimized.css');
            $expected = advagg_test_remove_sniffer_comments($expected);
            $optimized_output_url = advagg_load_stylesheet($file_url, TRUE);
            $this->assertEqual($optimized_output_url, $expected, format_string('Optimized CSS file (loaded from an URL) has expected contents (@file)', array(
                '@file' => $file,
            )));
        }
        // File. Tests: charset*.css
        // - Any @charaset declaration at the beginning of a file should be
        //   removed without breaking subsequent CSS.
        $testfiles = array(
            'charset.css',
            'charset_newline.css',
            'charset_sameline.css',
        );
        $path = drupal_get_path('module', 'advagg') . '/tests/css_test_files';
        foreach ($testfiles as $file) {
            $file_path = $path . '/' . $file;
            $file_url = $GLOBALS['base_url'] . '/' . $file_path;
            $expected = advagg_file_get_contents($file_path . '.optimized.css');
            $expected = advagg_test_remove_sniffer_comments($expected);
            $optimized_output = advagg_load_stylesheet($file_path, TRUE);
            $this->assertEqual($optimized_output, $expected, format_string('Optimized CSS file has expected contents (@file)', array(
                '@file' => $file,
            )));
            $expected = advagg_file_get_contents($file_path . '.optimized.css');
            $expected = advagg_test_remove_sniffer_comments($expected);
            $optimized_output_url = advagg_load_stylesheet($file_url, TRUE);
            $this->assertEqual($optimized_output_url, $expected, format_string('Optimized CSS file (loaded from an URL) has expected contents (@file)', array(
                '@file' => $file,
            )));
        }
        // Set all to FALSE.
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
        $GLOBALS['conf']['advagg_skip_file_create_url_inside_css'] = FALSE;
        $settings_to_change = array(
            '' => '',
            'advagg_skip_file_create_url_inside_css' => TRUE,
            'advagg_convert_absolute_to_relative_path' => TRUE,
            'advagg_convert_absolute_to_protocol_relative_path' => TRUE,
            'advagg_force_https_path' => TRUE,
        );
        $advagg_path = drupal_get_path('module', 'advagg');
        $path = $advagg_path . '/tests/css_test_files';
        foreach ($settings_to_change as $name => $value) {
            $before = '';
            if (!empty($name)) {
                $before = variable_get($name, defined(strtoupper($name)) ? constant(strtoupper($name)) : NULL);
                $GLOBALS['conf'][$name] = $value;
            }
            // File. Tests: advagg.css
            // - Various url() tests.
            //   https://www.drupal.org/node/1514182
            //   https://www.drupal.org/node/1961340
            //   https://www.drupal.org/node/2362643
            //   https://www.drupal.org/node/2112067
            $testfiles = array(
                'advagg.css',
            );
            foreach ($testfiles as $testfile) {
                $base_url_before = $GLOBALS['base_url'];
                $GLOBALS['base_url'] = advagg_force_http_path($GLOBALS['base_url']);
                $aggregate_settings = array(
                    'variables' => array(
                        'is_https' => FALSE,
                        'base_path' => $GLOBALS['base_path'] === '/checkout/' ? $GLOBALS['base_path'] : $GLOBALS['base_path'] . 'advagg_base_path_test/',
                        'advagg_convert_absolute_to_relative_path' => $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'],
                        'advagg_convert_absolute_to_protocol_relative_path' => $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'],
                        'advagg_force_https_path' => $GLOBALS['conf']['advagg_force_https_path'],
                        'advagg_skip_file_create_url_inside_css' => $GLOBALS['conf']['advagg_skip_file_create_url_inside_css'],
                    ),
                );
                if (module_exists('cdn')) {
                    $aggregate_settings['variables'][CDN_MODE_VARIABLE] = CDN_DISABLED;
                    $aggregate_settings['variables'][CDN_STATUS_VARIABLE] = CDN_DISABLED;
                }
                $file_path = $path . '/' . $testfile;
                $files = array(
                    'file' => $file_path,
                    'external' => $GLOBALS['base_url'] . '/' . $file_path,
                );
                $expected = advagg_test_remove_sniffer_comments(advagg_file_get_contents($file_path . '.optimized.css'));
                foreach ($files as $type => $file) {
                    $optimized_output = advagg_load_css_stylesheet($file, TRUE, $aggregate_settings);
                    $mode = 0;
                    if ($aggregate_settings['variables']['advagg_skip_file_create_url_inside_css'] && $type !== 'external') {
                        $mode = "01";
                        $optimized_output = str_replace($aggregate_settings['variables']['base_path'] . $advagg_path . '/', '', $optimized_output);
                    }
                    elseif (!$aggregate_settings['variables']['advagg_convert_absolute_to_relative_path'] && !$aggregate_settings['variables']['advagg_convert_absolute_to_protocol_relative_path'] && !$aggregate_settings['variables']['advagg_force_https_path']) {
                        $mode = 4;
                        $optimized_output = str_replace('http://' . $_SERVER['HTTP_HOST'] . $aggregate_settings['variables']['base_path'] . $advagg_path . '/', '', $optimized_output);
                    }
                    elseif ($aggregate_settings['variables']['advagg_convert_absolute_to_protocol_relative_path']) {
                        $mode = 2;
                        $optimized_output = str_replace('//' . $_SERVER['HTTP_HOST'] . $aggregate_settings['variables']['base_path'] . $advagg_path . '/', '', $optimized_output);
                    }
                    elseif ($aggregate_settings['variables']['advagg_force_https_path']) {
                        $mode = 3;
                        $optimized_output = str_replace('https://' . $_SERVER['HTTP_HOST'] . $aggregate_settings['variables']['base_path'] . $advagg_path . '/', '', $optimized_output);
                    }
                    else {
                        $mode = 1;
                        $optimized_output = str_replace($aggregate_settings['variables']['base_path'] . $advagg_path . '/', '', $optimized_output);
                    }
                    $this->assertEqual($optimized_output, $expected, format_string("Optimized CSS file has expected contents (@file). Setting tested: @name; value before: @before, value after: @after.<br>mode: @mode. <p>!replacements</p> <p><code>!debug</code></p>", array(
                        '@file' => $file,
                        '@name' => $name,
                        '@before' => is_bool($before) || strlen((string) $before) == 0 ? strtoupper(var_export($before, TRUE)) : $before,
                        '@after' => is_bool($value) || strlen((string) $value) == 0 ? strtoupper(var_export($value, TRUE)) : $value,
                        '@mode' => $mode,
                        '!replacements' => "1: {$aggregate_settings['variables']['base_path']}{$advagg_path}/ <br> 2: //{$_SERVER['HTTP_HOST']}{$aggregate_settings['variables']['base_path']}{$advagg_path}/ <br> 3: https://{$_SERVER['HTTP_HOST']}{$aggregate_settings['variables']['base_path']}{$advagg_path}/ <br> 4: http://{$_SERVER['HTTP_HOST']}{$aggregate_settings['variables']['base_path']}{$advagg_path}/",
                        '!debug' => nl2br(str_replace(' ', '&nbsp;', $optimized_output)),
                    )));
                    $GLOBALS['base_url'] = $base_url_before;
                }
            }
            if (!empty($name)) {
                $GLOBALS['conf'][$name] = $before;
            }
        }
    }

}

/**
 * Tests for the JavaScript system.
 */
class AdvAggJavaScriptTestCase extends DrupalWebTestCase {
    
    /**
     * Store configured value for JavaScript preprocessing.
     *
     * @var bool
     */
    protected $preprocessJs = NULL;
    
    /**
     * Provide information to the UI for this test.
     */
    public static function getInfo() {
        return array(
            'name' => 'JavaScript',
            'description' => 'Tests the JavaScript system.',
            'group' => 'AdvAgg',
        );
    }
    
    /**
     * Install the advagg module and include needed files.
     */
    public function setUp() {
        // Enable Locale and SimpleTest in the test environment.
        parent::setUp(array(
            'locale',
            'locale_test',
            'simpletest',
            'common_test',
            'form_test',
            'advagg',
            'color',
        ));
        // Include the advagg.module file.
        drupal_load('module', 'advagg');
        module_load_include('inc', 'advagg', 'advagg');
        // Disable preprocessing.
        $this->preprocessJs = variable_get('preprocess_js', 0);
        variable_set('preprocess_js', 0);
        // Reset before each test.
        advagg_test_reset_statics();
        // Set settings for testing.
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
        $GLOBALS['conf']['advagg_mod_js_footer'] = 0;
    }
    
    /**
     * Restore any variables we set.
     */
    public function tearDown() {
        // Restore configured value for JavaScript preprocessing.
        variable_set('preprocess_js', $this->preprocessJs);
        parent::tearDown();
    }
    
    /**
     * Test the 'javascript_always_use_jquery' variable.
     */
    public function testJavaScriptAlwaysUsejQuery() {
        // The default front page of the site should use jQuery and other standard
        // scripts and settings.
        advagg_test_reset_statics();
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
        $GLOBALS['conf']['advagg_mod_js_footer'] = 0;
        $this->drupalGet('');
        $this->assertRaw('misc/jquery.js', 'Default behavior: The front page of the site includes jquery.js.');
        $this->assertRaw('misc/drupal.js', 'Default behavior: The front page of the site includes drupal.js.');
        $this->assertRaw('Drupal.settings', 'Default behavior: The front page of the site includes Drupal settings.');
        $this->assertRaw('basePath', 'Default behavior: The front page of the site includes the basePath Drupal setting.');
        //
        // The default front page should not use jQuery and other standard scripts
        // and settings when the 'javascript_always_use_jquery' variable is set to
        // FALSE.
        advagg_test_reset_statics();
        variable_set('javascript_always_use_jquery', FALSE);
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->drupalGet('');
        $this->assertNoRaw('misc/jquery.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include jquery.js.');
        $this->assertNoRaw('misc/drupal.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include drupal.js.');
        $this->assertNoRaw('Drupal.settings', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings.');
        $this->assertNoRaw('basePath', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include the basePath Drupal setting.');
        variable_del('javascript_always_use_jquery');
        //
        // When only settings have been added via drupal_add_js(), drupal_get_js()
        // should still return jQuery and other standard scripts and settings.
        advagg_test_reset_statics();
        drupal_add_js(array(
            'testJavaScriptSetting' => 'test',
        ), 'setting');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the added Drupal settings.');
        //
        // When only settings have been added via drupal_add_js() and the
        // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
        // should not return jQuery and other standard scripts and settings, nor
        // should it return the requested settings (since they cannot actually be
        // added to the page without jQuery).
        advagg_test_reset_statics();
        variable_set('javascript_always_use_jquery', FALSE);
        drupal_add_js(array(
            'testJavaScriptSetting' => 'test',
        ), 'setting');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the added Drupal settings.');
        variable_del('javascript_always_use_jquery');
        //
        // When a regular file has been added via drupal_add_js(), drupal_get_js()
        // should return jQuery and other standard scripts and settings.
        advagg_test_reset_statics();
        drupal_add_js('misc/collapse.js');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
        //
        // When a regular file has been added via drupal_add_js() and the
        // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
        // should still return jQuery and other standard scripts and settings
        // (since the file is assumed to require jQuery by default).
        advagg_test_reset_statics();
        variable_set('javascript_always_use_jquery', FALSE);
        drupal_add_js('misc/collapse.js');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
        variable_del('javascript_always_use_jquery');
        //
        // When a file that does not require jQuery has been added via
        // drupal_add_js(), drupal_get_js() should still return jQuery and other
        // standard scripts and settings by default.
        advagg_test_reset_statics();
        drupal_add_js('misc/collapse.js', array(
            'requires_jquery' => FALSE,
        ));
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
        //
        // When a file that does not require jQuery has been added via
        // drupal_add_js() and the 'javascript_always_use_jquery' variable is set
        // to FALSE, drupal_get_js() should not return jQuery and other standard
        // scripts and setting, but it should still return the requested file.
        advagg_test_reset_statics();
        variable_set('javascript_always_use_jquery', FALSE);
        drupal_add_js('misc/collapse.js', array(
            'requires_jquery' => FALSE,
        ));
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
        variable_del('javascript_always_use_jquery');
        //
        // When 'javascript_always_use_jquery' is set to FALSE and a file that does
        // not require jQuery is added, followed by one that does, drupal_get_js()
        // should return jQuery and other standard scripts and settings, in
        // addition to both of the requested files.
        advagg_test_reset_statics();
        variable_set('javascript_always_use_jquery', FALSE);
        drupal_add_js('misc/collapse.js', array(
            'requires_jquery' => FALSE,
        ));
        drupal_add_js('misc/ajax.js');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes jquery.js.');
        $this->assertTrue(strpos($javascript, 'misc/drupal.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes drupal.js.');
        $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes Drupal.settings.');
        $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the basePath Drupal setting.');
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the first custom file.');
        $this->assertTrue(strpos($javascript, 'misc/ajax.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the second custom file.');
        variable_del('javascript_always_use_jquery');
        //
        // Tests JavaScript aggregation when files are added to a different scope.
        advagg_test_reset_statics();
        // Enable JavaScript aggregation.
        variable_set('preprocess_js', 1);
        // Add two JavaScript files to the current request and build the cache.
        drupal_add_js('misc/ajax.js');
        drupal_add_js('misc/autocomplete.js');
        $js_items = drupal_add_js();
        drupal_build_js_cache(array(
            'misc/ajax.js' => $js_items['misc/ajax.js'],
            'misc/autocomplete.js' => $js_items['misc/autocomplete.js'],
        ));
        // Store the expected key for the first item in the cache.
        $cache = array_keys(variable_get('drupal_js_cache_files', array()));
        $expected_key = $cache[0];
        // Reset variables and add a file in a different scope first.
        variable_del('drupal_js_cache_files');
        advagg_test_reset_statics();
        drupal_add_js('some/custom/javascript_file.js', array(
            'scope' => 'footer',
        ));
        drupal_add_js('misc/ajax.js');
        drupal_add_js('misc/autocomplete.js');
        // Rebuild the cache.
        $js_items = drupal_add_js();
        drupal_build_js_cache(array(
            'misc/ajax.js' => $js_items['misc/ajax.js'],
            'misc/autocomplete.js' => $js_items['misc/autocomplete.js'],
        ));
        // Compare the expected key for the first file to the current one.
        $cache = array_keys(variable_get('drupal_js_cache_files', array()));
        $key = $cache[0];
        $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.');
        variable_set('preprocess_js', 0);
        //
        // Test JavaScript ordering.
        advagg_test_reset_statics();
        // 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.
"-5 #1",
            "0 #1",
            "0 #2",
            "0 #3",
            "5 #1",
            "5 #2",
        );
        // Retrieve the rendered JavaScript and test against the regex.
        $render_array = advagg_get_js('footer');
        $js = drupal_render($render_array);
        $matches = array();
        if (preg_match_all('/Weight\\s([-0-9]+\\s[#0-9]+)/', $js, $matches)) {
            $result = $matches[1];
        }
        else {
            $result = array();
        }
        $this->assertIdentical($result, $expected, 'JavaScript is added in the expected weight order.');
        //
        // Test default JavaScript is empty.
        advagg_test_reset_statics();
        $this->assertEqual(array(), drupal_add_js(), 'Default JavaScript is empty.');
        //
        // Test drupal_get_js() for JavaScript settings.
        advagg_test_reset_statics();
        // 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');
        // All three of these entries should appear in Drupal.settings.
        drupal_add_js(array(
            'commonTestArray' => array(
                'commonTestValue0',
            ),
        ), 'setting');
        drupal_add_js(array(
            'commonTestArray' => array(
                'commonTestValue1',
            ),
        ), 'setting');
        drupal_add_js(array(
            'commonTestArray' => array(
                'commonTestValue2',
            ),
        ), 'setting');
        // Only the second of these two entries should appear in Drupal.settings.
        drupal_add_js(array(
            'commonTestArray' => array(
                'key' => 'commonTestOldValue',
            ),
        ), 'setting');
        drupal_add_js(array(
            'commonTestArray' => array(
                'key' => 'commonTestNewValue',
            ),
        ), 'setting');
        $render_array = advagg_get_js('header');
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'basePath') > 0, 'Rendered JavaScript header returns basePath setting.');
        $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, 'Rendered JavaScript header includes jQuery.');
        $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, 'Rendered JavaScript header returns pathPrefix setting.');
        // Test whether drupal_add_js can be used to override a previous setting.
        $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.');
        // Test whether drupal_add_js can be used to add numerically indexed values
        // to an array.
        $array_values_appear = strpos($javascript, 'commonTestValue0') > 0 && strpos($javascript, 'commonTestValue1') > 0 && strpos($javascript, 'commonTestValue2') > 0;
        $this->assertTrue($array_values_appear, 'drupal_add_js() correctly adds settings to the end of an indexed array.');
        // Test whether drupal_add_js can be used to override the entry for an
        // existing key in an associative array.
        $associative_array_override = strpos($javascript, 'commonTestNewValue') > 0 && strpos($javascript, 'commonTestOldValue') === FALSE;
        $this->assertTrue($associative_array_override, 'drupal_add_js() correctly overrides settings within an associative array.');
        //
        // Test rendering an external JavaScript file.
        advagg_test_reset_statics();
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
        $external = 'http://example.com/example.js';
        drupal_add_js($external, 'external');
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        // Local files have a base_path() prefix, external files should not.
        $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, 'Rendering an external JavaScript file.');
        //
        // Test drupal_get_js() with a footer scope.
        advagg_test_reset_statics();
        $inline = 'jQuery(function () { });';
        drupal_add_js($inline, array(
            'type' => 'inline',
            'scope' => 'footer',
        ));
        $render_array = advagg_get_js('footer');
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, $inline) > 0, 'Rendered JavaScript footer returns the inline code.');
        //
        // Ensures that vertical-tabs.js is included before collapse.js.
        advagg_test_reset_statics();
        $this->drupalGet('form_test/vertical-tabs');
        $position1 = strpos($this->content, 'misc/vertical-tabs.js');
        $position2 = strpos($this->content, 'misc/collapse.js');
        $this->assertTrue($position1 !== FALSE && $position2 !== FALSE && $position1 < $position2, 'vertical-tabs.js is included before collapse.js');
        //
        // Test rendering the JavaScript with a file's weight above jQuery's.
        advagg_test_reset_statics();
        // 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.
        drupal_add_js('misc/collapse.js', array(
            'group' => JS_LIBRARY,
            'every_page' => TRUE,
            'weight' => -21,
        ));
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), 'Rendering a JavaScript file above jQuery.');
        //
        // Test altering a JavaScript's weight via hook_js_alter().
        advagg_test_reset_statics();
        // Add both tableselect.js and simpletest.js, with a larger weight on
        // SimpleTest.
        drupal_add_js('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.
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
        //
        // Adds a library to the page and tests for both its JavaScript and its CSS.
        advagg_test_reset_statics();
        $result = drupal_add_library('system', 'farbtastic');
        $this->assertTrue($result !== FALSE, 'Library was added without errors.');
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $render_array = advagg_get_css();
        $styles = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), 'JavaScript of library was added to the page.');
        $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), 'Stylesheet of library was added to the page.');
        //
        // Adds a JavaScript library to the page and alters it.
        advagg_test_reset_statics();
        // Verify that common_test altered the title of Farbtastic.
        $library = drupal_get_library('system', 'farbtastic');
        $this->assertEqual($library['title'], 'Farbtastic: Altered Library', 'Registered libraries were altered.');
        // common_test_library_alter() also added a dependency on jQuery Form.
        drupal_add_library('system', 'farbtastic');
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), 'Altered library dependencies are added to the page.');
        //
        // Tests non-existing libraries.
        advagg_test_reset_statics();
        $result = drupal_get_library('unknown', 'unknown');
        $this->assertFalse($result, 'Unknown library returned FALSE.');
        advagg_test_reset_statics();
        $result = drupal_add_library('unknown', 'unknown');
        $this->assertFalse($result, 'Unknown library returned FALSE.');
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, 'unknown') === FALSE, 'Unknown library was not added to the page.');
        //
        // Tests the addition of libraries through the #attached['library']
        // property.
        advagg_test_reset_statics();
        $element['#attached']['library'][] = array(
            'system',
            'farbtastic',
        );
        drupal_render($element);
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), 'The attached_library property adds the additional libraries.');
        //
        // Test #attached functionality in children elements.
        advagg_test_reset_statics();
        // The cache system is turned off for POST requests.
        $request_method = $_SERVER['REQUEST_METHOD'];
        $_SERVER['REQUEST_METHOD'] = 'GET';
        // Create an element with a child and subchild.  Each element loads a
        // different JavaScript file using #attached.
        $parent_js = drupal_get_path('module', 'user') . '/user.js';
        $child_js = drupal_get_path('module', 'color') . '/color.js';
        $subchild_js = drupal_get_path('module', 'book') . '/book.js';
        $element = array(
            '#type' => 'fieldset',
            '#cache' => array(
                'keys' => array(
                    'simpletest',
                    'drupal_render',
                    'children_attached',
                ),
            ),
            '#attached' => array(
                'js' => array(
                    $parent_js,
                ),
            ),
            '#title' => 'Parent',
        );
        $element['child'] = array(
            '#type' => 'fieldset',
            '#attached' => array(
                'js' => array(
                    $child_js,
                ),
            ),
            '#title' => 'Child',
        );
        $element['child']['subchild'] = array(
            '#attached' => array(
                'js' => array(
                    $subchild_js,
                ),
            ),
            '#markup' => 'Subchild',
        );
        // Render the element and verify the presence of #attached JavaScript.
        drupal_render($element);
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included.');
        $this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included.');
        $this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included.');
        // Load the element from cache and verify the presence of the #attached
        // JavaScript.
        advagg_test_reset_statics();
        $this->assertTrue(drupal_render_cache_get($element), 'The element was retrieved from cache.');
        $render_array = advagg_get_js();
        $scripts = drupal_render($render_array);
        $this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included when loading from cache.');
        $this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included when loading from cache.');
        $this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included when loading from cache.');
        $_SERVER['REQUEST_METHOD'] = $request_method;
        //
        // Tests the localisation of JavaScript libraries. Verifies that the
        // datepicker can be localized.
        advagg_test_reset_statics();
        drupal_add_library('system', 'ui.datepicker');
        $GLOBALS['conf']['advagg_mod_js_footer'] = 0;
        $render_array = advagg_get_js();
        $javascript = drupal_render($render_array);
        $this->assertTrue(strpos($javascript, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.');
        //
        // Functional tests for JavaScript parsing for translatable strings.
        // Tests parsing js files for translatable strings.
        advagg_test_reset_statics();
        $filename = drupal_get_path('module', 'locale_test') . '/locale_test.js';
        // Parse the file to look for source strings.
        _locale_parse_js_file($filename);
        // Get all of the source strings that were found.
        $source_strings = db_select('locales_source', 's')->fields('s', array(
            'source',
            'context',
        ))
            ->condition('s.location', $filename)
            ->execute()
            ->fetchAllKeyed();
        // List of all strings that should be in the file.
        $test_strings = array(
            "Standard Call t" => '',
            "Whitespace Call t" => '',
            "Single Quote t" => '',
            "Single Quote \\'Escaped\\' t" => '',
            "Single Quote Concat strings t" => '',
            "Double Quote t" => '',
            "Double Quote \\\"Escaped\\\" t" => '',
            "Double Quote Concat strings t" => '',
            "Context !key Args t" => "Context string",
            "Context Unquoted t" => "Context string unquoted",
            "Context Single Quoted t" => "Context string single quoted",
            "Context Double Quoted t" => "Context string double quoted",
            "Standard Call plural" => '',
            "Standard Call @count plural" => '',
            "Whitespace Call plural" => '',
            "Whitespace Call @count plural" => '',
            "Single Quote plural" => '',
            "Single Quote @count plural" => '',
            "Single Quote \\'Escaped\\' plural" => '',
            "Single Quote \\'Escaped\\' @count plural" => '',
            "Double Quote plural" => '',
            "Double Quote @count plural" => '',
            "Double Quote \\\"Escaped\\\" plural" => '',
            "Double Quote \\\"Escaped\\\" @count plural" => '',
            "Context !key Args plural" => "Context string",
            "Context !key Args @count plural" => "Context string",
            "Context Unquoted plural" => "Context string unquoted",
            "Context Unquoted @count plural" => "Context string unquoted",
            "Context Single Quoted plural" => "Context string single quoted",
            "Context Single Quoted @count plural" => "Context string single quoted",
            "Context Double Quoted plural" => "Context string double quoted",
            "Context Double Quoted @count plural" => "Context string double quoted",
        );
        // Assert that all strings were found properly.
        foreach ($test_strings as $str => $context) {
            $args = array(
                '%source' => $str,
                '%context' => $context,
            );
            // Make sure that the string was found in the file.
            $this->assertTrue(isset($source_strings[$str]), format_string('Found source string: %source', $args));
            // Make sure that the proper context was matched.
            $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? format_string('Context for %source is %context', $args) : format_string('Context for %source is blank', $args));
        }
        $this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.');
        //
        // Adds a language and checks that the JavaScript translation files are
        // properly created and rebuilt on deletion.
        $user = $this->drupalCreateUser(array(
            'translate interface',
            'administer languages',
            'access administration pages',
        ));
        $this->drupalLogin($user);
        $langcode = 'xx';
        // The English name for the language. This will be translated.
        $name = $this->randomName(16);
        // The native name for the language.
        $native = $this->randomName(16);
        // The domain prefix.
        $prefix = $langcode;
        // Add custom language.
        $edit = array(
            'langcode' => $langcode,
            'name' => $name,
            'native' => $native,
            'prefix' => $prefix,
            'direction' => '0',
        );
        $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
        drupal_static_reset('language_list');
        // Build the JavaScript translation file.
        $this->drupalGet('admin/config/regional/translate/translate');
        // Retrieve the id of the first string available in the {locales_source}
        // table and translate it.
        $query = db_select('locales_source', 'l');
        $query->addExpression('min(l.lid)', 'lid');
        $result = $query->condition('l.location', '%.js%', 'LIKE')
            ->condition('l.textgroup', 'default')
            ->execute();
        $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid;
        $edit = array(
            'translations[' . $langcode . ']' => $this->randomName(),
        );
        $this->drupalPost($url, $edit, t('Save translations'));
        // Trigger JavaScript translation parsing and building.
        require_once DRUPAL_ROOT . '/includes/locale.inc';
        _locale_rebuild_js($langcode);
        // Retrieve the JavaScript translation hash code for the custom language to
        // check that the translation file has been properly built.
        $file = db_select('languages', 'l')->fields('l', array(
            'javascript',
        ))
            ->condition('language', $langcode)
            ->execute()
            ->fetchObject();
        $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/' . $langcode . '_' . $file->javascript . '.js';
        $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file created: %file', array(
            '%file' => $result ? $js_file : 'not found',
        )));
        // Test JavaScript translation rebuilding.
        file_unmanaged_delete($js_file);
        $this->assertTrue($result = !file_exists($js_file), format_string('JavaScript file deleted: %file', array(
            '%file' => $result ? $js_file : 'found',
        )));
        cache_clear_all();
        _locale_rebuild_js($langcode);
        $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file rebuilt: %file', array(
            '%file' => $result ? $js_file : 'not found',
        )));
    }

}

/**
 * Unit tests for the Theme API.
 */
class AdvAggThemeTestCase extends AJAXTestCase {
    
    /**
     * Provide information to the UI for this test.
     */
    public static function getInfo() {
        return array(
            'name' => 'Theme API and AJAX',
            'description' => 'Test low-level theme functions and AJAX framework functions and commands.',
            'group' => 'AdvAgg',
        );
    }
    
    /**
     * Install the advagg module and include needed files.
     */
    public function setUp() {
        parent::setUp(array(
            'advagg',
        ));
        // Include the advagg.module file.
        drupal_load('module', 'advagg');
        module_load_include('inc', 'advagg', 'advagg');
        module_enable(array(
            'theme_test',
        ));
        theme_enable(array(
            'test_theme',
        ));
        // Reset before each test.
        advagg_test_reset_statics();
        // Set settings for testing.
        $GLOBALS['conf']['advagg_convert_absolute_to_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_convert_absolute_to_protocol_relative_path'] = FALSE;
        $GLOBALS['conf']['advagg_force_https_path'] = FALSE;
    }
    
    /**
     * Check theme and ajax functions and commands.
     */
    public function testCssOverride() {
        // Ensures a theme's .info file is able to override a module CSS file from
        // being added to the page.
        advagg_test_reset_statics();
        // Reuse the same page as in testPreprocessForSuggestions(). We're testing
        // what is output to the HTML HEAD based on what is in a theme's .info file,
        // so it doesn't matter what page we get, as long as it is themed with the
        // test theme. First we test with CSS aggregation disabled.
        $GLOBALS['conf']['preprocess_css'] = 0;
        variable_set('preprocess_css', 0);
        $this->drupalGet('theme-test/suggestion');
        $this->assertNoText('system.base.css', 'The theme\'s .info file is able to override a module CSS file from being added to the page.');
        // Also test with aggregation enabled, simply ensuring no PHP errors are
        // triggered during drupal_build_css_cache() when a source file doesn't
        // exist. Then allow remaining tests to continue with aggregation disabled
        // by default.
        variable_set('preprocess_css', 1);
        $this->drupalGet('theme-test/suggestion');
        variable_set('preprocess_css', 0);
        //
        // Test ajax and js settings.
        advagg_test_reset_statics();
        $commands = $this->drupalGetAJAX('ajax-test/render');
        // Verify that there is a command to load settings added with
        // drupal_add_js().
        $expected = array(
            'command' => 'settings',
            'settings' => array(
                'basePath' => base_path(),
                'ajax' => 'test',
            ),
        );
        $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));
        // Verify that Ajax settings are loaded for #type 'link'.
        $this->drupalGet('ajax-test/link');
        $settings = $this->drupalGetSettings();
        $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips'));
        $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main');
        //
        // Test behavior of ajax_render_error().
        // Reset before each test.
        advagg_test_reset_statics();
        // Verify default error message.
        $commands = $this->drupalGetAJAX('ajax-test/render-error');
        $expected = array(
            'command' => 'alert',
            'text' => t('An error occurred while handling the request: The server received invalid input.'),
        );
        $this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.'));
        // Verify custom error message.
        $edit = array(
            'message' => 'Custom error message.',
        );
        $commands = $this->drupalGetAJAX('ajax-test/render-error', array(
            'query' => $edit,
        ));
        $expected = array(
            'command' => 'alert',
            'text' => $edit['message'],
        );
        $this->assertCommand($commands, $expected, t('Custom error message is output.'));
        //
        // Test that new JavaScript and CSS files added during an AJAX request are
        // returned.
        // Reset before each test.
        advagg_test_reset_statics();
        $expected = array(
            'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
            'setting_value' => 'executed',
            'css' => drupal_get_path('module', 'system') . '/system.admin.css',
            'js' => drupal_get_path('module', 'system') . '/system.js',
        );
        // @todo D8: Add a drupal_css_defaults() helper function.
        $expected_css_render_array = advagg_get_css(array(
            $expected['css'] => array(
                'type' => 'file',
                'group' => CSS_DEFAULT,
                'weight' => 0,
                'every_page' => FALSE,
                'media' => 'all',
                'preprocess' => TRUE,
                'data' => $expected['css'],
                'browsers' => array(
                    'IE' => TRUE,
                    '!IE' => TRUE,
                ),
            ),
        ), TRUE);
        $expected_css_html = drupal_render($expected_css_render_array);
        $expected_js_render_array = advagg_get_js('header', array(
            $expected['js'] => drupal_js_defaults($expected['js']),
        ), TRUE);
        $expected_js_html = drupal_render($expected_js_render_array);
        // Get the base page.
        $this->drupalGet('ajax_forms_test_lazy_load_form');
        $original_settings = $this->drupalGetSettings();
        $original_css = $original_settings['ajaxPageState']['css'];
        $original_js = $original_settings['ajaxPageState']['js'];
        // Verify that the base page doesn't have the settings and files that are to
        // be lazy loaded as part of the next requests.
        $this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array(
            '%setting' => $expected['setting_name'],
        )));
        $this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array(
            '%css' => $expected['css'],
        )));
        $this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array(
            '%js' => $expected['js'],
        )));
        // Submit the AJAX request without triggering files getting added.
        $commands = $this->drupalPostAJAX(NULL, array(
            'add_files' => FALSE,
        ), array(
            'op' => t('Submit'),
        ));
        $new_settings = $this->drupalGetSettings();
        // Verify the setting was not added when not expected.
        $this->assertTrue(!isset($new_settings['setting_name']), t('Page still lacks the %setting, as expected.', array(
            '%setting' => $expected['setting_name'],
        )));
        // Verify a settings command does not add CSS or scripts to Drupal.settings
        // and no command inserts the corresponding tags on the page.
        $found_settings_command = FALSE;
        $found_markup_command = FALSE;
        foreach ($commands as $command) {
            if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
                $found_settings_command = TRUE;
            }
            if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
                $found_markup_command = TRUE;
            }
        }
        $this->assertFalse($found_settings_command, t('Page state still lacks the %css and %js files, as expected.', array(
            '%css' => $expected['css'],
            '%js' => $expected['js'],
        )));
        $this->assertFalse($found_markup_command, t('Page still lacks the %css and %js files, as expected.', array(
            '%css' => $expected['css'],
            '%js' => $expected['js'],
        )));
        // Submit the AJAX request and trigger adding files.
        $commands = $this->drupalPostAJAX(NULL, array(
            'add_files' => TRUE,
        ), array(
            'op' => t('Submit'),
        ));
        $new_settings = $this->drupalGetSettings();
        $new_css = $new_settings['ajaxPageState']['css'];
        $new_js = $new_settings['ajaxPageState']['js'];
        // Verify the expected setting was added.
        $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array(
            '%setting' => $expected['setting_name'],
        )));
        // Verify the expected CSS file was added, both to Drupal.settings, and as
        // an AJAX command for inclusion into the HTML.
        $this->assertEqual($new_css, $original_css + array(
            $expected['css'] => 1,
        ), t('Page state now has the %css file.', array(
            '%css' => $expected['css'],
        )));
        $this->assertCommand($commands, array(
            'data' => $expected_css_html,
        ), t('Page now has the %css file.', array(
            '%css' => $expected['css'],
        )));
        // Verify the expected JS file was added, both to Drupal.settings, and as
        // an AJAX command for inclusion into the HTML. By testing for an exact HTML
        // string containing the SCRIPT tag, we also ensure that unexpected
        // JavaScript code, such as a jQuery.extend() that would potentially clobber
        // rather than properly merge settings, didn't accidentally get added.
        $this->assertEqual($new_js, $original_js + array(
            $expected['js'] => 1,
        ), t('Page state now has the %js file.', array(
            '%js' => $expected['js'],
        )));
        $this->assertCommand($commands, array(
            'data' => $expected_js_html,
        ), t('Page now has the %js file.', array(
            '%js' => $expected['js'],
        )));
        //
        // Tests that overridden CSS files are not added during lazy load.
        // Reset before each test.
        advagg_test_reset_statics();
        // The test theme overrides system.base.css without an implementation,
        // thereby removing it.
        theme_enable(array(
            'test_theme',
        ));
        variable_set('theme_default', 'test_theme');
        // This gets the form, and emulates an Ajax submission on it, including
        // adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
        $this->drupalPostAJAX('ajax_forms_test_lazy_load_form', array(
            'add_files' => TRUE,
        ), array(
            'op' => t('Submit'),
        ));
        // Verify that the resulting HTML does not load the overridden CSS file.
        // We add a "?" to the assertion, because Drupal.settings may include
        // information about the file; we only really care about whether it appears
        // in a LINK or STYLE tag, for which Drupal always adds a query string for
        // cache control.
        $this->assertNoText('system.base.css?', 'Ajax lazy loading does not add overridden CSS files.');
        //
        // Test the various Ajax Commands.
        // Reset before each test.
        advagg_test_reset_statics();
        $form_path = 'ajax_forms_test_ajax_commands_form';
        $web_user = $this->drupalCreateUser(array(
            'access content',
        ));
        $this->drupalLogin($web_user);
        $edit = array();
        // Tests the 'after' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'After': Click to put something after the div"),
        ));
        $expected = array(
            'command' => 'insert',
            'method' => 'after',
            'data' => 'This will be placed after',
        );
        $this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data");
        // Tests the 'alert' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'Alert': Click to alert"),
        ));
        $expected = array(
            'command' => 'alert',
            'text' => 'Alert',
        );
        $this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text");
        // Tests the 'append' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'Append': Click to append something"),
        ));
        $expected = array(
            'command' => 'insert',
            'method' => 'append',
            'data' => 'Appended text',
        );
        $this->assertCommand($commands, $expected, "'append' AJAX command issued with correct data");
        // Tests the 'before' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'before': Click to put something before the div"),
        ));
        $expected = array(
            'command' => 'insert',
            'method' => 'before',
            'data' => 'Before text',
        );
        $this->assertCommand($commands, $expected, "'before' AJAX command issued with correct data");
        // Tests the 'changed' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX changed: Click to mark div changed."),
        ));
        $expected = array(
            'command' => 'changed',
            'selector' => '#changed_div',
        );
        $this->assertCommand($commands, $expected, "'changed' AJAX command issued with correct selector");
        // Tests the 'changed' command using the second argument.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX changed: Click to mark div changed with asterisk."),
        ));
        $expected = array(
            'command' => 'changed',
            'selector' => '#changed_div',
            'asterisk' => '#changed_div_mark_this',
        );
        $this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector");
        // Tests the 'css' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("Set the '#box' div to be blue."),
        ));
        $expected = array(
            'command' => 'css',
            'selector' => '#css_div',
            'argument' => array(
                'background-color' => 'blue',
            ),
        );
        $this->assertCommand($commands, $expected, "'css' AJAX command issued with correct selector");
        // Tests the 'data' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX data command: Issue command."),
        ));
        $expected = array(
            'command' => 'data',
            'name' => 'testkey',
            'value' => 'testvalue',
        );
        $this->assertCommand($commands, $expected, "'data' AJAX command issued with correct key and value");
        // Tests the 'invoke' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX invoke command: Invoke addClass() method."),
        ));
        $expected = array(
            'command' => 'invoke',
            'method' => 'addClass',
            'arguments' => array(
                'error',
            ),
        );
        $this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument");
        // Tests the 'html' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX html: Replace the HTML in a selector."),
        ));
        $expected = array(
            'command' => 'insert',
            'method' => 'html',
            'data' => 'replacement text',
        );
        $this->assertCommand($commands, $expected, "'html' AJAX command issued with correct data");
        // Tests the 'insert' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX insert: Let client insert based on #ajax['method']."),
        ));
        $expected = array(
            'command' => 'insert',
            'data' => 'insert replacement text',
        );
        $this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data");
        // Tests the 'prepend' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'prepend': Click to prepend something"),
        ));
        $expected = array(
            'command' => 'insert',
            'method' => 'prepend',
            'data' => 'prepended text',
        );
        $this->assertCommand($commands, $expected, "'prepend' AJAX command issued with correct data");
        // Tests the 'remove' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'remove': Click to remove text"),
        ));
        $expected = array(
            'command' => 'remove',
            'selector' => '#remove_text',
        );
        $this->assertCommand($commands, $expected, "'remove' AJAX command issued with correct command and selector");
        // Tests the 'restripe' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'restripe' command"),
        ));
        $expected = array(
            'command' => 'restripe',
            'selector' => '#restripe_table',
        );
        $this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector");
        // Tests the 'settings' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'settings' command"),
        ));
        $expected = array(
            'command' => 'settings',
            'settings' => array(
                'ajax_forms_test' => array(
                    'foo' => 42,
                ),
            ),
        );
        $this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
        // Tests the 'add_css' command.
        $commands = $this->drupalPostAJAX($form_path, $edit, array(
            'op' => t("AJAX 'add_css' command"),
        ));
        $expected = array(
            'command' => 'add_css',
            'data' => 'my/file.css',
        );
        $this->assertCommand($commands, $expected, "'add_css' AJAX command issued with correct data");
    }

}

/**
 * @} End of "defgroup advagg_tests".
 */

Functions

Title Deprecated Summary
advagg_test_generate_selector_css Generates a large CSS string.
advagg_test_randon_string Generates random string.
advagg_test_remove_sniffer_comments Strip the codingStandardsIgnoreFile string from the input.
advagg_test_reset_statics Resets static variables related to adding CSS or JS to a page.

Classes

Title Deprecated Summary
AdvAggCascadingStylesheetsTestCase Test the Drupal CSS system.
AdvAggJavaScriptTestCase Tests for the JavaScript system.
AdvAggThemeTestCase Unit tests for the Theme API.