MediaWiki  REL1_22
JavaScriptMinifierTest.php
Go to the documentation of this file.
00001 <?php
00002 
00003 class JavaScriptMinifierTest extends MediaWikiTestCase {
00004 
00005     public static function provideCases() {
00006         return array(
00007 
00008             // Basic whitespace and comments that should be stripped entirely
00009             array( "\r\t\f \v\n\r", "" ),
00010             array( "/* Foo *\n*bar\n*/", "" ),
00011 
00017             array( "/**\n * Foo\n * {\n * 'bar' : {\n * //Multiple rules with configurable operators\n * 'baz' : false\n * }\n */", "" ),
00018 
00023             array( "'  Foo  \\'  bar  \\\n  baz  \\'  quox  '  .length", "'  Foo  \\'  bar  \\\n  baz  \\'  quox  '.length" ),
00024             array( "\"  Foo  \\\"  bar  \\\n  baz  \\\"  quox  \"  .length", "\"  Foo  \\\"  bar  \\\n  baz  \\\"  quox  \".length" ),
00025             array( "// Foo b/ar baz", "" ),
00026             array( "/  Foo  \\/  bar  [  /  \\]  /  ]  baz  /  .length", "/  Foo  \\/  bar  [  /  \\]  /  ]  baz  /.length" ),
00027 
00028             // HTML comments
00029             array( "<!-- Foo bar", "" ),
00030             array( "<!-- Foo --> bar", "" ),
00031             array( "--> Foo", "" ),
00032             array( "x --> y", "x-->y" ),
00033 
00034             // Semicolon insertion
00035             array( "(function(){return\nx;})", "(function(){return\nx;})" ),
00036             array( "throw\nx;", "throw\nx;" ),
00037             array( "while(p){continue\nx;}", "while(p){continue\nx;}" ),
00038             array( "while(p){break\nx;}", "while(p){break\nx;}" ),
00039             array( "var\nx;", "var x;" ),
00040             array( "x\ny;", "x\ny;" ),
00041             array( "x\n++y;", "x\n++y;" ),
00042             array( "x\n!y;", "x\n!y;" ),
00043             array( "x\n{y}", "x\n{y}" ),
00044             array( "x\n+y;", "x+y;" ),
00045             array( "x\n(y);", "x(y);" ),
00046             array( "5.\nx;", "5.\nx;" ),
00047             array( "0xFF.\nx;", "0xFF.x;" ),
00048             array( "5.3.\nx;", "5.3.x;" ),
00049 
00050             // Semicolon insertion between an expression having an inline
00051             // comment after it, and a statement on the next line (bug 27046).
00052             array( "var a = this //foo bar \n for ( b = 0; c < d; b++ ) {}", "var a=this\nfor(b=0;c<d;b++){}" ),
00053 
00054             // Token separation
00055             array( "x  in  y", "x in y" ),
00056             array( "/x/g  in  y", "/x/g in y" ),
00057             array( "x  in  30", "x in 30" ),
00058             array( "x  +  ++  y", "x+ ++y" ),
00059             array( "x ++  +  y", "x++ +y" ),
00060             array( "x  /  /y/.exec(z)", "x/ /y/.exec(z)" ),
00061 
00062             // State machine
00063             array( "/  x/g", "/  x/g" ),
00064             array( "(function(){return/  x/g})", "(function(){return/  x/g})" ),
00065             array( "+/  x/g", "+/  x/g" ),
00066             array( "++/  x/g", "++/  x/g" ),
00067             array( "x/  x/g", "x/x/g" ),
00068             array( "(/  x/g)", "(/  x/g)" ),
00069             array( "if(/  x/g);", "if(/  x/g);" ),
00070             array( "(x/  x/g)", "(x/x/g)" ),
00071             array( "([/  x/g])", "([/  x/g])" ),
00072             array( "+x/  x/g", "+x/x/g" ),
00073             array( "{}/  x/g", "{}/  x/g" ),
00074             array( "+{}/  x/g", "+{}/x/g" ),
00075             array( "(x)/  x/g", "(x)/x/g" ),
00076             array( "if(x)/  x/g", "if(x)/  x/g" ),
00077             array( "for(x;x;{}/  x/g);", "for(x;x;{}/x/g);" ),
00078             array( "x;x;{}/  x/g", "x;x;{}/  x/g" ),
00079             array( "x:{}/  x/g", "x:{}/  x/g" ),
00080             array( "switch(x){case y?z:{}/  x/g:{}/  x/g;}", "switch(x){case y?z:{}/x/g:{}/  x/g;}" ),
00081             array( "function x(){}/  x/g", "function x(){}/  x/g" ),
00082             array( "+function x(){}/  x/g", "+function x(){}/x/g" ),
00083 
00084             // Multiline quoted string
00085             array( "var foo=\"\\\nblah\\\n\";", "var foo=\"\\\nblah\\\n\";" ),
00086 
00087             // Multiline quoted string followed by string with spaces
00088             array( "var foo=\"\\\nblah\\\n\";\nvar baz = \" foo \";\n", "var foo=\"\\\nblah\\\n\";var baz=\" foo \";" ),
00089 
00090             // URL in quoted string ( // is not a comment)
00091             array( "aNode.setAttribute('href','http://foo.bar.org/baz');", "aNode.setAttribute('href','http://foo.bar.org/baz');" ),
00092 
00093             // URL in quoted string after multiline quoted string
00094             array( "var foo=\"\\\nblah\\\n\";\naNode.setAttribute('href','http://foo.bar.org/baz');", "var foo=\"\\\nblah\\\n\";aNode.setAttribute('href','http://foo.bar.org/baz');" ),
00095 
00096             // Division vs. regex nastiness
00097             array( "alert( (10+10) / '/'.charCodeAt( 0 ) + '//' );", "alert((10+10)/'/'.charCodeAt(0)+'//');" ),
00098             array( "if(1)/a /g.exec('Pa ss');", "if(1)/a /g.exec('Pa ss');" ),
00099 
00100             // newline insertion after 1000 chars: break after the "++", not before
00101             array( str_repeat( ';', 996 ) . "if(x++);", str_repeat( ';', 996 ) . "if(x++\n);" ),
00102 
00103             // Unicode letter characters should pass through ok in identifiers (bug 31187)
00104             array( "var KaŝSkatolVal = {}", 'var KaŝSkatolVal={}' ),
00105 
00106             // Per spec unicode char escape values should work in identifiers,
00107             // as long as it's a valid char. In future it might get normalized.
00108             array( "var Ka\\u015dSkatolVal = {}", 'var Ka\\u015dSkatolVal={}' ),
00109 
00110             // Some structures that might look invalid at first sight
00111             array( "var a = 5.;", "var a=5.;" ),
00112             array( "5.0.toString();", "5.0.toString();" ),
00113             array( "5..toString();", "5..toString();" ),
00114             array( "5...toString();", false ),
00115             array( "5.\n.toString();", '5..toString();' ),
00116         );
00117     }
00118 
00122     public function testJavaScriptMinifierOutput( $code, $expectedOutput ) {
00123         $minified = JavaScriptMinifier::minify( $code );
00124 
00125         // JSMin+'s parser will throw an exception if output is not valid JS.
00126         // suppression of warnings needed for stupid crap
00127         wfSuppressWarnings();
00128         $parser = new JSParser();
00129         wfRestoreWarnings();
00130         $parser->parse( $minified, 'minify-test.js', 1 );
00131 
00132         $this->assertEquals( $expectedOutput, $minified, "Minified output should be in the form expected." );
00133     }
00134 
00135     public static function provideBug32548() {
00136         return array(
00137             array(
00138                 // This one gets interpreted all together by the prior code;
00139                 // no break at the 'E' happens.
00140                 '1.23456789E55',
00141             ),
00142             array(
00143                 // This one breaks under the bad code; splits between 'E' and '+'
00144                 '1.23456789E+5',
00145             ),
00146             array(
00147                 // This one breaks under the bad code; splits between 'E' and '-'
00148                 '1.23456789E-5',
00149             ),
00150         );
00151     }
00152 
00156     public function testBug32548Exponent( $num ) {
00157         // Long line breaking was being incorrectly done between the base and
00158         // exponent part of a number, causing a syntax error. The line should
00159         // instead break at the start of the number.
00160         $prefix = 'var longVarName' . str_repeat( '_', 973 ) . '=';
00161         $suffix = ',shortVarName=0;';
00162 
00163         $input = $prefix . $num . $suffix;
00164         $expected = $prefix . "\n" . $num . $suffix;
00165 
00166         $minified = JavaScriptMinifier::minify( $input );
00167 
00168         $this->assertEquals( $expected, $minified, "Line breaks must not occur in middle of exponent" );
00169     }
00170 }