PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/CSSParserTests.php

http://github.com/sabberworm/PHP-CSS-Parser
PHP | 283 lines | 255 code | 21 blank | 7 comment | 41 complexity | 723d7b3b38b2fe8479cfd9093825a03d MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. // !!!! Do not edit this File with TextMate. Saving will corrupt one Unicode character (U+1D11E).
  3. require_once(dirname(__FILE__).'/../CSSParser.php');
  4. class CSSParserTests extends PHPUnit_Framework_TestCase {
  5. function testCssFiles() {
  6. $sDirectory = dirname(__FILE__).DIRECTORY_SEPARATOR.'files';
  7. if($rHandle = opendir($sDirectory)) {
  8. /* This is the correct way to loop over the directory. */
  9. while (false !== ($sFileName = readdir($rHandle))) {
  10. if(strpos($sFileName, '.') === 0) {
  11. continue;
  12. }
  13. if(strrpos($sFileName, '.css') !== strlen($sFileName)-strlen('.css')) {
  14. continue;
  15. }
  16. if(strpos($sFileName, '-') === 0) {
  17. //Either a file which SHOULD fail or a future test of a as-of-now missing feature
  18. continue;
  19. }
  20. $oParser = new CSSParser(file_get_contents($sDirectory.DIRECTORY_SEPARATOR.$sFileName));
  21. try {
  22. $this->assertNotEquals('', $oParser->parse()->__toString());
  23. } catch(Exception $e) {
  24. $this->fail($e);
  25. }
  26. }
  27. closedir($rHandle);
  28. }
  29. }
  30. /**
  31. * @depends testCssFiles
  32. */
  33. function testColorParsing() {
  34. $oDoc = $this->parsedStructureForFile('colortest');
  35. foreach($oDoc->getAllRuleSets() as $oRuleSet) {
  36. if(!$oRuleSet instanceof CSSDeclarationBlock) {
  37. continue;
  38. }
  39. $sSelector = $oRuleSet->getSelectors();
  40. $sSelector = $sSelector[0]->getSelector();
  41. if($sSelector == '#mine') {
  42. $aColorRule = $oRuleSet->getRules('color');
  43. $aValues = $aColorRule['color']->getValues();
  44. $this->assertSame('red', $aValues[0][0]);
  45. $aColorRule = $oRuleSet->getRules('background-');
  46. $aValues = $aColorRule['background-color']->getValues();
  47. $this->assertEquals(array('r' => new CSSSize(35.0, null, true), 'g' => new CSSSize(35.0, null, true), 'b' => new CSSSize(35.0, null, true)), $aValues[0][0]->getColor());
  48. $aColorRule = $oRuleSet->getRules('border-color');
  49. $aValues = $aColorRule['border-color']->getValues();
  50. $this->assertEquals(array('r' => new CSSSize(10.0, null, true), 'g' => new CSSSize(100.0, null, true), 'b' => new CSSSize(230.0, null, true), 'a' => new CSSSize(0.3, null, true)), $aValues[0][0]->getColor());
  51. $aColorRule = $oRuleSet->getRules('outline-color');
  52. $aValues = $aColorRule['outline-color']->getValues();
  53. $this->assertEquals(array('r' => new CSSSize(34.0, null, true), 'g' => new CSSSize(34.0, null, true), 'b' => new CSSSize(34.0, null, true)), $aValues[0][0]->getColor());
  54. }
  55. }
  56. foreach($oDoc->getAllValues('background-') as $oColor) {
  57. if($oColor->getColorDescription() === 'hsl') {
  58. $this->assertEquals(array('h' => new CSSSize(220.0, null, true), 's' => new CSSSize(10.0, null, true), 'l' => new CSSSize(220.0, null, true)), $oColor->getColor());
  59. }
  60. }
  61. foreach($oDoc->getAllValues('color') as $sColor) {
  62. $this->assertSame('red', $sColor);
  63. }
  64. }
  65. function testUnicodeParsing() {
  66. $oDoc = $this->parsedStructureForFile('unicode');
  67. foreach($oDoc->getAllDeclarationBlocks() as $oRuleSet) {
  68. $sSelector = $oRuleSet->getSelectors();
  69. $sSelector = $sSelector[0]->getSelector();
  70. if(substr($sSelector, 0, strlen('.test-')) !== '.test-') {
  71. continue;
  72. }
  73. $aContentRules = $oRuleSet->getRules('content');
  74. $aContents = $aContentRules['content']->getValues();
  75. $sCssString = $aContents[0][0]->__toString();
  76. if($sSelector == '.test-1') {
  77. $this->assertSame('" "', $sCssString);
  78. }
  79. if($sSelector == '.test-2') {
  80. $this->assertSame('"?Š"', $sCssString);
  81. }
  82. if($sSelector == '.test-3') {
  83. $this->assertSame('" "', $sCssString);
  84. }
  85. if($sSelector == '.test-4') {
  86. $this->assertSame('"??&#x201E;&#x17E;"', $sCssString);
  87. }
  88. if($sSelector == '.test-5') {
  89. $this->assertSame('"?°´"', $sCssString);
  90. }
  91. if($sSelector == '.test-6') {
  92. $this->assertSame('"Â?"', $sCssString);
  93. }
  94. if($sSelector == '.test-7') {
  95. $this->assertSame('"\A"', $sCssString);
  96. }
  97. if($sSelector == '.test-8') {
  98. $this->assertSame('"\"\""', $sCssString);
  99. }
  100. if($sSelector == '.test-9') {
  101. $this->assertSame('"\"\\\'"', $sCssString);
  102. }
  103. if($sSelector == '.test-10') {
  104. $this->assertSame('"\\\'\\\\"', $sCssString);
  105. }
  106. if($sSelector == '.test-11') {
  107. $this->assertSame('"test"', $sCssString);
  108. }
  109. }
  110. }
  111. function testSpecificity() {
  112. $oDoc = $this->parsedStructureForFile('specificity');
  113. $oDeclarationBlock = $oDoc->getAllDeclarationBlocks();
  114. $oDeclarationBlock = $oDeclarationBlock[0];
  115. $aSelectors = $oDeclarationBlock->getSelectors();
  116. foreach($aSelectors as $oSelector) {
  117. switch($oSelector->getSelector()) {
  118. case "#test .help":
  119. $this->assertSame(110, $oSelector->getSpecificity());
  120. break;
  121. case "#file":
  122. $this->assertSame(100, $oSelector->getSpecificity());
  123. break;
  124. case ".help:hover":
  125. $this->assertSame(20, $oSelector->getSpecificity());
  126. break;
  127. case "ol li::before":
  128. $this->assertSame(3, $oSelector->getSpecificity());
  129. break;
  130. case "li.green":
  131. $this->assertSame(11, $oSelector->getSpecificity());
  132. break;
  133. default:
  134. $this->fail("specificity: untested selector ".$oSelector->getSelector());
  135. }
  136. }
  137. $this->assertEquals(array(new CSSSelector('#test .help', true)), $oDoc->getSelectorsBySpecificity('> 100'));
  138. }
  139. function testManipulation() {
  140. $oDoc = $this->parsedStructureForFile('atrules');
  141. $this->assertSame('@charset "utf-8";@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}html, body {font-size: 1.6em;}'."\n", $oDoc->__toString());
  142. foreach($oDoc->getAllDeclarationBlocks() as $oBlock) {
  143. foreach($oBlock->getSelectors() as $oSelector) {
  144. //Loop over all selector parts (the comma-separated strings in a selector) and prepend the id
  145. $oSelector->setSelector('#my_id '.$oSelector->getSelector());
  146. }
  147. }
  148. $this->assertSame('@charset "utf-8";@font-face {font-family: "CrassRoots";src: url("../media/cr.ttf");}#my_id html, #my_id body {font-size: 1.6em;}'."\n", $oDoc->__toString());
  149. $oDoc = $this->parsedStructureForFile('values');
  150. $this->assertSame('#header {margin: 10px 2em 1cm 2%;font-family: Verdana,Helvetica,"Gill Sans",sans-serif;font-size: 10px;color: red !important;}
  151. body {color: green;font: 75% "Lucida Grande","Trebuchet MS",Verdana,sans-serif;}'."\n", $oDoc->__toString());
  152. foreach($oDoc->getAllRuleSets() as $oRuleSet) {
  153. $oRuleSet->removeRule('font-');
  154. }
  155. $this->assertSame('#header {margin: 10px 2em 1cm 2%;color: red !important;}
  156. body {color: green;}'."\n", $oDoc->__toString());
  157. }
  158. function testSlashedValues() {
  159. $oDoc = $this->parsedStructureForFile('slashed');
  160. $this->assertSame('.test {font: 12px/1.5 Verdana,Arial,sans-serif;border-radius: 5px 10px 5px 10px/10px 5px 10px 5px;}'."\n", $oDoc->__toString());
  161. foreach($oDoc->getAllValues(null) as $mValue) {
  162. if($mValue instanceof CSSSize && $mValue->isSize() && !$mValue->isRelative()) {
  163. $mValue->setSize($mValue->getSize()*3);
  164. }
  165. }
  166. foreach($oDoc->getAllDeclarationBlocks() as $oBlock) {
  167. $oRule = $oBlock->getRules('font');
  168. $oRule = $oRule['font'];
  169. $oSpaceList = $oRule->getValue();
  170. $this->assertEquals(' ', $oSpaceList->getListSeparator());
  171. $oSlashList = $oSpaceList->getListComponents();
  172. $oCommaList = $oSlashList[1];
  173. $oSlashList = $oSlashList[0];
  174. $this->assertEquals(',', $oCommaList->getListSeparator());
  175. $this->assertEquals('/', $oSlashList->getListSeparator());
  176. $oRule = $oBlock->getRules('border-radius');
  177. $oRule = $oRule['border-radius'];
  178. $oSlashList = $oRule->getValue();
  179. $this->assertEquals('/', $oSlashList->getListSeparator());
  180. $oSpaceList1 = $oSlashList->getListComponents();
  181. $oSpaceList2 = $oSpaceList1[1];
  182. $oSpaceList1 = $oSpaceList1[0];
  183. $this->assertEquals(' ', $oSpaceList1->getListSeparator());
  184. $this->assertEquals(' ', $oSpaceList2->getListSeparator());
  185. }
  186. $this->assertSame('.test {font: 36px/1.5 Verdana,Arial,sans-serif;border-radius: 15px 30px 15px 30px/30px 15px 30px 15px;}'."\n", $oDoc->__toString());
  187. }
  188. function testFunctionSyntax() {
  189. $oDoc = $this->parsedStructureForFile('functions');
  190. $sExpected = 'div.main {background-image: linear-gradient(rgb(0,0,0),rgb(255,255,255));}
  191. .collapser::before, .collapser::-moz-before, .collapser::-webkit-before {content: "Â?";font-size: 1.2em;margin-right: 0.2em;-moz-transition-property: -moz-transform;-moz-transition-duration: 0.2s;-moz-transform-origin: center 60%;}
  192. .collapser.expanded::before, .collapser.expanded::-moz-before, .collapser.expanded::-webkit-before {-moz-transform: rotate(90deg);}
  193. .collapser + * {height: 0;overflow: hidden;-moz-transition-property: height;-moz-transition-duration: 0.3s;}
  194. .collapser.expanded + * {height: auto;}'."\n";
  195. $this->assertSame($sExpected, $oDoc->__toString());
  196. foreach($oDoc->getAllValues(null, true) as $mValue) {
  197. if($mValue instanceof CSSSize && $mValue->isSize()) {
  198. $mValue->setSize($mValue->getSize()*3);
  199. }
  200. }
  201. $sExpected = str_replace(array('1.2em', '0.2em', '60%'), array('3.6em', '0.6em', '180%'), $sExpected);
  202. $this->assertSame($sExpected, $oDoc->__toString());
  203. foreach($oDoc->getAllValues(null, true) as $mValue) {
  204. if($mValue instanceof CSSSize && !$mValue->isRelative() && !$mValue->isColorComponent()) {
  205. $mValue->setSize($mValue->getSize()*2);
  206. }
  207. }
  208. $sExpected = str_replace(array('0.2s', '0.3s', '90deg'), array('0.4s', '0.6s', '180deg'), $sExpected);
  209. $this->assertSame($sExpected, $oDoc->__toString());
  210. }
  211. function testExpandShorthands() {
  212. $oDoc = $this->parsedStructureForFile('expand-shorthands');
  213. $sExpected = 'body {font: italic 500 14px/1.618 "Trebuchet MS",Georgia,serif;border: 2px solid rgb(255,0,255);background: rgb(204,204,204) url("/images/foo.png") no-repeat left top;margin: 1em !important;padding: 2px 6px 3px;}'."\n";
  214. $this->assertSame($sExpected, $oDoc->__toString());
  215. $oDoc->expandShorthands();
  216. $sExpected = 'body {margin-top: 1em !important;margin-right: 1em !important;margin-bottom: 1em !important;margin-left: 1em !important;padding-top: 2px;padding-right: 6px;padding-bottom: 3px;padding-left: 6px;border-top-color: rgb(255,0,255);border-right-color: rgb(255,0,255);border-bottom-color: rgb(255,0,255);border-left-color: rgb(255,0,255);border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-left-style: solid;border-top-width: 2px;border-right-width: 2px;border-bottom-width: 2px;border-left-width: 2px;font-style: italic;font-variant: normal;font-weight: 500;font-size: 14px;line-height: 1.618;font-family: "Trebuchet MS",Georgia,serif;background-color: rgb(204,204,204);background-image: url("/images/foo.png");background-repeat: no-repeat;background-attachment: scroll;background-position: left top;}'."\n";
  217. $this->assertSame($sExpected, $oDoc->__toString());
  218. }
  219. function testCreateShorthands() {
  220. $oDoc = $this->parsedStructureForFile('create-shorthands');
  221. $sExpected = 'body {font-size: 2em;font-family: Helvetica,Arial,sans-serif;font-weight: bold;border-width: 2px;border-color: rgb(153,153,153);border-style: dotted;background-color: rgb(255,255,255);background-image: url("foobar.png");background-repeat: repeat-y;margin-top: 2px;margin-right: 3px;margin-bottom: 4px;margin-left: 5px;}'."\n";
  222. $this->assertSame($sExpected, $oDoc->__toString());
  223. $oDoc->createShorthands();
  224. $sExpected = 'body {background: rgb(255,255,255) url("foobar.png") repeat-y;margin: 2px 5px 4px 3px;border: 2px dotted rgb(153,153,153);font: bold 2em Helvetica,Arial,sans-serif;}'."\n";
  225. $this->assertSame($sExpected, $oDoc->__toString());
  226. }
  227. function testPrefixedGradient() {
  228. $oDoc = $this->parsedStructureForFile('webkit');
  229. $sExpected = '.test {background: -webkit-linear-gradient(top right,white,black);}'."\n";
  230. $this->assertSame($sExpected, $oDoc->__toString());
  231. }
  232. function testListValueRemoval() {
  233. $oDoc = $this->parsedStructureForFile('atrules');
  234. foreach($oDoc->getContents() as $oItem) {
  235. if($oItem instanceof CSSAtRule) {
  236. $oDoc->remove($oItem);
  237. break;
  238. }
  239. }
  240. $this->assertSame('@charset "utf-8";html, body {font-size: 1.6em;}'."\n", $oDoc->__toString());
  241. $oDoc = $this->parsedStructureForFile('nested');
  242. foreach($oDoc->getAllDeclarationBlocks() as $oBlock) {
  243. $oDoc->removeDeclarationBlockBySelector($oBlock, false);
  244. break;
  245. }
  246. $this->assertSame('html {some-other: -test(val1);}
  247. @media screen {html {some: -test(val2);}
  248. }#unrelated {other: yes;}'."\n", $oDoc->__toString());
  249. $oDoc = $this->parsedStructureForFile('nested');
  250. foreach($oDoc->getAllDeclarationBlocks() as $oBlock) {
  251. $oDoc->removeDeclarationBlockBySelector($oBlock, true);
  252. break;
  253. }
  254. $this->assertSame('@media screen {html {some: -test(val2);}
  255. }#unrelated {other: yes;}'."\n", $oDoc->__toString());
  256. }
  257. function parsedStructureForFile($sFileName) {
  258. $sFile = dirname(__FILE__).DIRECTORY_SEPARATOR.'files'.DIRECTORY_SEPARATOR."$sFileName.css";
  259. $oParser = new CSSParser(file_get_contents($sFile));
  260. return $oParser->parse();
  261. }
  262. }