MediaWiki  REL1_20
JavaScriptMinifierTest.php
Go to the documentation of this file.
00001 <?php
00002 
00003 class JavaScriptMinifierTest extends MediaWikiTestCase {
00004 
00005         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         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         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         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 }