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