// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.cssomTest'); goog.setTestOnly('goog.cssomTest'); goog.require('goog.array'); goog.require('goog.cssom'); goog.require('goog.cssom.CssRuleType'); goog.require('goog.testing.jsunit'); goog.require('goog.userAgent'); // Since sheet cssom_test1.css's first line is to import // cssom_test2.css, we should get 2 before one in the string. var cssText = '.css-link-1 { display: block; } ' + '.css-import-2 { display: block; } ' + '.css-import-1 { display: block; } ' + '.css-style-1 { display: block; } ' + '.css-style-2 { display: block; } ' + '.css-style-3 { display: block; }'; var replacementCssText = '.css-repl-1 { display: block; }'; var isIe7 = goog.userAgent.IE && (goog.userAgent.compare(goog.userAgent.VERSION, '7.0') == 0); // We're going to toLowerCase cssText before testing, because IE returns // CSS property names in UPPERCASE, and the function shouldn't // "fix" the text as it would be expensive and rarely of use. // Same goes for the trailing whitespace in IE. // Same goes for fixing the optimized removal of trailing ; in rules. // Also needed for Opera. function fixCssTextForIe(cssText) { cssText = cssText.toLowerCase().replace(/\s*$/, ''); if (cssText.match(/[^;] \}/)) { cssText = cssText.replace(/([^;]) \}/g, '$1; }'); } return cssText; } function testGetFileNameFromStyleSheet() { var styleSheet = {'href': 'http://foo.com/something/filename.css'}; assertEquals( 'filename.css', goog.cssom.getFileNameFromStyleSheet(styleSheet)); styleSheet = {'href': 'https://foo.com:123/something/filename.css'}; assertEquals( 'filename.css', goog.cssom.getFileNameFromStyleSheet(styleSheet)); styleSheet = {'href': 'http://foo.com/something/filename.css?bar=bas'}; assertEquals( 'filename.css', goog.cssom.getFileNameFromStyleSheet(styleSheet)); styleSheet = {'href': 'filename.css?bar=bas'}; assertEquals( 'filename.css', goog.cssom.getFileNameFromStyleSheet(styleSheet)); styleSheet = {'href': 'filename.css'}; assertEquals( 'filename.css', goog.cssom.getFileNameFromStyleSheet(styleSheet)); } function testGetAllCssStyleSheets() { var styleSheets = goog.cssom.getAllCssStyleSheets(); assertEquals(4, styleSheets.length); // Makes sure they're in the right cascade order. assertEquals( 'cssom_test_link_1.css', goog.cssom.getFileNameFromStyleSheet(styleSheets[0])); assertEquals( 'cssom_test_import_2.css', goog.cssom.getFileNameFromStyleSheet(styleSheets[1])); assertEquals( 'cssom_test_import_1.css', goog.cssom.getFileNameFromStyleSheet(styleSheets[2])); // Not an external styleSheet assertNull(goog.cssom.getFileNameFromStyleSheet(styleSheets[3])); } function testGetAllCssText() { var allCssText = goog.cssom.getAllCssText(); // In IE7, a CSSRule object gets included twice and replaces another // existing CSSRule object. We aren't using // goog.testing.ExpectedFailures since it brings in additional CSS // which breaks a lot of our expectations about the number of rules // present in a style sheet. if (!isIe7) { assertEquals(cssText, fixCssTextForIe(allCssText)); } } function testGetAllCssStyleRules() { var allCssRules = goog.cssom.getAllCssStyleRules(); assertEquals(6, allCssRules.length); } function testAddCssText() { var newCssText = '.css-add-1 { display: block; }'; var newCssNode = goog.cssom.addCssText(newCssText); assertEquals(document.styleSheets.length, 3); var allCssText = goog.cssom.getAllCssText(); // In IE7, a CSSRule object gets included twice and replaces another // existing CSSRule object. We aren't using // goog.testing.ExpectedFailures since it brings in additional CSS // which breaks a lot of our expectations about the number of rules // present in a style sheet. if (!isIe7) { // Opera inserts the CSSRule to the first position. And fixCssText // is also needed to clean up whitespace. if (goog.userAgent.OPERA) { assertEquals(newCssText + ' ' + cssText, fixCssTextForIe(allCssText)); } else { assertEquals(cssText + ' ' + newCssText, fixCssTextForIe(allCssText)); } } var cssRules = goog.cssom.getAllCssStyleRules(); assertEquals(7, cssRules.length); // Remove the new stylesheet now so it doesn't interfere with other // tests. newCssNode.parentNode.removeChild(newCssNode); // Sanity check. cssRules = goog.cssom.getAllCssStyleRules(); assertEquals(6, cssRules.length); } function testAddCssRule() { // test that addCssRule correctly adds the rule to the style // sheet. var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var newCssRule = '.css-addCssRule { display: block; }'; var rules = styleSheet.rules || styleSheet.cssRules; var origNumberOfRules = rules.length; goog.cssom.addCssRule(styleSheet, newCssRule, 1); rules = styleSheet.rules || styleSheet.cssRules; var newNumberOfRules = rules.length; assertEquals(newNumberOfRules, origNumberOfRules + 1); // Remove the added rule so we don't mess up other tests. goog.cssom.removeCssRule(styleSheet, 1); } function testAddCssRuleAtPos() { // test that addCssRule correctly adds the rule to the style // sheet at the specified position. var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var newCssRule = '.css-addCssRulePos { display: block; }'; var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var origNumberOfRules = rules.length; // Firefox croaks if we try to insert a CSSRule at an index that // contains a CSSImport Rule. Since we deal only with CSSStyleRule // objects, we find the first CSSStyleRule and return its index. // // NOTE(user): We could have unified the code block below for all // browsers but IE6 horribly mangled up the stylesheet by creating // duplicate instances of a rule when removeCssRule was invoked // just after addCssRule with the looping construct in. This is // perfectly fine since IE's styleSheet.rules does not contain // references to anything but CSSStyleRules. var pos = 0; if (styleSheet.cssRules) { pos = goog.array.findIndex(rules, function(rule) { return rule.type == goog.cssom.CssRuleType.STYLE; }); } goog.cssom.addCssRule(styleSheet, newCssRule, pos); rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var newNumberOfRules = rules.length; assertEquals(newNumberOfRules, origNumberOfRules + 1); // Remove the added rule so we don't mess up other tests. goog.cssom.removeCssRule(styleSheet, pos); rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); assertEquals(origNumberOfRules, rules.length); } function testAddCssRuleNoIndex() { // How well do we handle cases where the optional index is // not passed in? var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var origNumberOfRules = rules.length; var newCssRule = '.css-addCssRuleNoIndex { display: block; }'; // Try inserting the rule without specifying an index. // Make sure we don't throw an exception, and that we added // the entry. goog.cssom.addCssRule(styleSheet, newCssRule); rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var newNumberOfRules = rules.length; assertEquals(newNumberOfRules, origNumberOfRules + 1); // Remove the added rule so we don't mess up the other tests. goog.cssom.removeCssRule(styleSheet, newNumberOfRules - 1); rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); assertEquals(origNumberOfRules, rules.length); } function testGetParentStyleSheetAfterGetAllCssStyleRules() { var cssRules = goog.cssom.getAllCssStyleRules(); var cssRule = cssRules[4]; var parentStyleSheet = goog.cssom.getParentStyleSheet(cssRule); var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; assertEquals(styleSheet, parentStyleSheet); } function testGetCssRuleIndexInParentStyleSheetAfterGetAllCssStyleRules() { var cssRules = goog.cssom.getAllCssStyleRules(); var cssRule = cssRules[4]; // Note here that this is correct - IE's styleSheet.rules does not // contain references to anything but CSSStyleRules while FF and others // include anything that inherits from the CSSRule interface. // See http://dev.w3.org/csswg/cssom/#cssrule. var parentStyleSheet = goog.cssom.getParentStyleSheet(cssRule); var ruleIndex = goog.isDefAndNotNull(parentStyleSheet.cssRules) ? 2 : 1; assertEquals( ruleIndex, goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule)); } function testGetCssRuleIndexInParentStyleSheetNonStyleRule() { // IE's styleSheet.rules only contain CSSStyleRules. if (!goog.userAgent.IE) { var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var newCssRule = '@media print { .css-nonStyle { display: block; } }'; goog.cssom.addCssRule(styleSheet, newCssRule); var rules = styleSheet.rules || styleSheet.cssRules; var cssRule = rules[rules.length - 1]; assertEquals(goog.cssom.CssRuleType.MEDIA, cssRule.type); // Make sure we don't throw an exception. goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule, styleSheet); // Remove the added rule. goog.cssom.removeCssRule(styleSheet, rules.length - 1); } } // Tests the scenario where we have a known stylesheet and index. function testReplaceCssRuleWithStyleSheetAndIndex() { var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var index = 2; var origCssRule = rules[index]; var origCssText = fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule)); goog.cssom.replaceCssRule(origCssRule, replacementCssText, styleSheet, index); var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var newCssRule = rules[index]; var newCssText = goog.cssom.getCssTextFromCssRule(newCssRule); assertEquals(replacementCssText, fixCssTextForIe(newCssText)); // Now we need to re-replace our rule, to preserve parity for the other // tests. goog.cssom.replaceCssRule(newCssRule, origCssText, styleSheet, index); var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var nowCssRule = rules[index]; var nowCssText = goog.cssom.getCssTextFromCssRule(nowCssRule); assertEquals(origCssText, fixCssTextForIe(nowCssText)); } function testReplaceCssRuleUsingGetAllCssStyleRules() { var cssRules = goog.cssom.getAllCssStyleRules(); var origCssRule = cssRules[4]; var origCssText = fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule)); // notice we don't pass in the stylesheet or index. goog.cssom.replaceCssRule(origCssRule, replacementCssText); var styleSheets = goog.cssom.getAllCssStyleSheets(); var styleSheet = styleSheets[3]; var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet); var index = goog.isDefAndNotNull(styleSheet.cssRules) ? 2 : 1; var newCssRule = rules[index]; var newCssText = fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule)); assertEquals(replacementCssText, newCssText); // try getting it the other way around too. var cssRules = goog.cssom.getAllCssStyleRules(); var newCssRule = cssRules[4]; var newCssText = fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule)); assertEquals(replacementCssText, newCssText); // Now we need to re-replace our rule, to preserve parity for the other // tests. goog.cssom.replaceCssRule(newCssRule, origCssText); var cssRules = goog.cssom.getAllCssStyleRules(); var nowCssRule = cssRules[4]; var nowCssText = fixCssTextForIe(goog.cssom.getCssTextFromCssRule(nowCssRule)); assertEquals(origCssText, nowCssText); }