Follow-up r103915: We need to increment $end before the strcspn.
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / JavaScriptMinifierTest.php
1 <?php
2
3 class JavaScriptMinifierTest extends MediaWikiTestCase {
4
5 function provideCases() {
6 return array(
7 // Basic tokens
8 array( "\r\t\f \v\n\r", "" ),
9 array( "/* Foo *\n*bar\n*/", "" ),
10 /**
11 * ' Foo \' bar \
12 * baz \' quox ' .
13 */
14 array( "' Foo \\' bar \\\n baz \\' quox ' .length", "' Foo \\' bar \\\n baz \\' quox '.length" ),
15 array( "\" Foo \\\" bar \\\n baz \\\" quox \" .length", "\" Foo \\\" bar \\\n baz \\\" quox \".length" ),
16 array( "// Foo b/ar baz", "" ),
17 array( "/ Foo \\/ bar [ / \\] / ] baz / .length", "/ Foo \\/ bar [ / \\] / ] baz /.length" ),
18 // HTML comments
19 array( "<!-- Foo bar", "" ),
20 array( "<!-- Foo --> bar", "" ),
21 array( "--> Foo", "" ),
22 array( "x --> y", "x-->y" ),
23 // Semicolon insertion
24 array( "(function(){return\nx;})", "(function(){return\nx;})" ),
25 array( "throw\nx;", "throw\nx;" ),
26 array( "while(p){continue\nx;}", "while(p){continue\nx;}" ),
27 array( "while(p){break\nx;}", "while(p){break\nx;}" ),
28 array( "var\nx;", "var x;" ),
29 array( "x\ny;", "x\ny;" ),
30 array( "x\n++y;", "x\n++y;" ),
31 array( "x\n!y;", "x\n!y;" ),
32 array( "x\n{y}", "x\n{y}" ),
33 array( "x\n+y;", "x+y;" ),
34 array( "x\n(y);", "x(y);" ),
35 array( "5.\nx;", "5.\nx;" ),
36 array( "0xFF.\nx;", "0xFF.x;" ),
37 array( "5.3.\nx;", "5.3.x;" ),
38 // Token separation
39 array( "x in y", "x in y" ),
40 array( "/x/g in y", "/x/g in y" ),
41 array( "x in 30", "x in 30" ),
42 array( "x + ++ y", "x+ ++y" ),
43 array( "x / /y/.exec(z)", "x/ /y/.exec(z)" ),
44 // State machine
45 array( "/ x/g", "/ x/g" ),
46 array( "(function(){return/ x/g})", "(function(){return/ x/g})" ),
47 array( "+/ x/g", "+/ x/g" ),
48 array( "++/ x/g", "++/ x/g" ),
49 array( "x/ x/g", "x/x/g" ),
50 array( "(/ x/g)", "(/ x/g)" ),
51 array( "if(/ x/g);", "if(/ x/g);" ),
52 array( "(x/ x/g)", "(x/x/g)" ),
53 array( "([/ x/g])", "([/ x/g])" ),
54 array( "+x/ x/g", "+x/x/g" ),
55 array( "{}/ x/g", "{}/ x/g" ),
56 array( "+{}/ x/g", "+{}/x/g" ),
57 array( "(x)/ x/g", "(x)/x/g" ),
58 array( "if(x)/ x/g", "if(x)/ x/g" ),
59 array( "for(x;x;{}/ x/g);", "for(x;x;{}/x/g);" ),
60 array( "x;x;{}/ x/g", "x;x;{}/ x/g" ),
61 array( "x:{}/ x/g", "x:{}/ x/g" ),
62 array( "switch(x){case y?z:{}/ x/g:{}/ x/g;}", "switch(x){case y?z:{}/x/g:{}/ x/g;}" ),
63 array( "function x(){}/ x/g", "function x(){}/ x/g" ),
64 array( "+function x(){}/ x/g", "+function x(){}/x/g" ),
65
66 // Tests for things that broke in the past
67 // Multiline quoted string
68 array( "var foo=\"\\\nblah\\\n\";", "var foo=\"\\\nblah\\\n\";" ),
69 // Multiline quoted string followed by string with spaces
70 array( "var foo=\"\\\nblah\\\n\";\nvar baz = \" foo \";\n", "var foo=\"\\\nblah\\\n\";var baz=\" foo \";" ),
71 // URL in quoted string ( // is not a comment)
72 array( "aNode.setAttribute('href','http://foo.bar.org/baz');", "aNode.setAttribute('href','http://foo.bar.org/baz');" ),
73 // URL in quoted string after multiline quoted string
74 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');" ),
75 // Division vs. regex nastiness
76 array( "alert( (10+10) / '/'.charCodeAt( 0 ) + '//' );", "alert((10+10)/'/'.charCodeAt(0)+'//');" ),
77 array( "if(1)/a /g.exec('Pa ss');", "if(1)/a /g.exec('Pa ss');" ),
78
79 // newline insertion after 1000 chars: break after the "++", not before
80 array( str_repeat( ';', 996 ) . "if(x++);", str_repeat( ';', 996 ) . "if(x++\n);" ),
81
82 // Unicode letter characters should pass through ok in identifiers (bug 31187)
83 array( "var KaŝSkatolVal = {}", 'var KaŝSkatolVal={}'),
84 // And also per spec unicode char escape values should work in identifiers,
85 // as long as it's a valid char. In future it might get normalized.
86 array( "var Ka\\u015dSkatolVal = {}", 'var Ka\\u015dSkatolVal={}'),
87 );
88 }
89
90 /**
91 * @dataProvider provideCases
92 */
93 function testJavaScriptMinifierOutput( $code, $expectedOutput ) {
94 $minified = JavaScriptMinifier::minify( $code );
95
96 // JSMin+'s parser will throw an exception if output is not valid JS.
97 // suppression of warnings needed for stupid crap
98 wfSuppressWarnings();
99 $parser = new JSParser();
100 wfRestoreWarnings();
101 $parser->parse( $minified, 'minify-test.js', 1 );
102
103 $this->assertEquals( $expectedOutput, $minified, "Minified output should be in the form expected." );
104 }
105
106 /**
107 * @dataProvider provideBug32548
108 */
109 function testBug32548Exponent($num) {
110 // Long line breaking was being incorrectly done between the base and
111 // exponent part of a number, causing a syntax error. The line should
112 // instead break at the start of the number.
113 $prefix = 'var longVarName' . str_repeat('_', 973) . '=';
114 $suffix = ',shortVarName=0;';
115
116 $input = $prefix . $num . $suffix;
117 $expected = $prefix . $num . "\n" . $suffix;
118
119 $minified = JavaScriptMinifier::minify( $input );
120
121 $this->assertEquals( $expected, $minified, "Line breaks must not occur in middle of exponent");
122 }
123
124 function provideBug32548() {
125 return array(
126 array(
127 // This one gets interpreted all together by the prior code;
128 // no break at the 'E' happens.
129 '1.23456789E55',
130 ),
131 array(
132 // This one breaks under the bad code; splits between 'E' and '+'
133 '1.23456789E+5',
134 ),
135 array(
136 // This one breaks under the bad code; splits between 'E' and '-'
137 '1.23456789E-5',
138 ),
139 );
140 }
141 }