moved language library to core mediawiki.jqueryMsg
[lhc/web/wiklou.git] / tests / jasmine / spec / mediawiki.jqueryMsg.spec.js
1 /* spec for language & message behaviour in MediaWiki */
2
3 mw.messages.set( {
4 "en_empty": "",
5 "en_simple": "Simple message",
6 "en_replace": "Simple $1 replacement",
7 "en_replace2": "Simple $1 $2 replacements",
8 "en_link": "Simple [http://example.com link to example].",
9 "en_link_replace": "Complex [$1 $2] behaviour.",
10 "en_simple_magic": "Simple {{ALOHOMORA}} message",
11 "en_undelete_short": "Undelete {{PLURAL:$1|one edit|$1 edits}}",
12 "en_undelete_empty_param": "Undelete{{PLURAL:$1|| multiple edits}}",
13 "en_category-subcat-count": "{{PLURAL:$2|This category has only the following subcategory.|This category has the following {{PLURAL:$1|subcategory|$1 subcategories}}, out of $2 total.}}",
14 "en_escape0": "Escape \\to fantasy island",
15 "en_escape1": "I had \\$2.50 in my pocket",
16 "en_escape2": "I had {{PLURAL:$1|the absolute \\|$1\\| which came out to \\$3.00 in my C:\\\\drive| some stuff}}",
17 "en_fail": "This should fail to {{parse",
18 "en_fail_magic": "There is no such magic word as {{SIETNAME}}"
19 } );
20
21 /**
22 * Tests
23 */
24 ( function( mw, $, undefined ) {
25
26 describe( "mediaWiki.jqueryMsg", function() {
27
28 describe( "basic message functionality", function() {
29
30 it( "should return identity for empty string", function() {
31 var parser = new mw.jqueryMsg.parser();
32 expect( parser.parse( 'en_empty' ).html() ).toEqual( '' );
33 } );
34
35
36 it( "should return identity for simple string", function() {
37 var parser = new mw.jqueryMsg.parser();
38 expect( parser.parse( 'en_simple' ).html() ).toEqual( 'Simple message' );
39 } );
40
41 } );
42
43 describe( "escaping", function() {
44
45 it ( "should handle simple escaping", function() {
46 var parser = new mw.jqueryMsg.parser();
47 expect( parser.parse( 'en_escape0' ).html() ).toEqual( 'Escape to fantasy island' );
48 } );
49
50 it ( "should escape dollar signs found in ordinary text when backslashed", function() {
51 var parser = new mw.jqueryMsg.parser();
52 expect( parser.parse( 'en_escape1' ).html() ).toEqual( 'I had $2.50 in my pocket' );
53 } );
54
55 it ( "should handle a complicated escaping case, including escaped pipe chars in template args", function() {
56 var parser = new mw.jqueryMsg.parser();
57 expect( parser.parse( 'en_escape2', [ 1 ] ).html() ).toEqual( 'I had the absolute |1| which came out to $3.00 in my C:\\drive' );
58 } );
59
60 } );
61
62 describe( "replacing", function() {
63
64 it ( "should handle simple replacing", function() {
65 var parser = new mw.jqueryMsg.parser();
66 expect( parser.parse( 'en_replace', [ 'foo' ] ).html() ).toEqual( 'Simple foo replacement' );
67 } );
68
69 it ( "should return $n if replacement not there", function() {
70 var parser = new mw.jqueryMsg.parser();
71 expect( parser.parse( 'en_replace', [] ).html() ).toEqual( 'Simple $1 replacement' );
72 expect( parser.parse( 'en_replace2', [ 'bar' ] ).html() ).toEqual( 'Simple bar $2 replacements' );
73 } );
74
75 } );
76
77 describe( "linking", function() {
78
79 it ( "should handle a simple link", function() {
80 var parser = new mw.jqueryMsg.parser();
81 var parsed = parser.parse( 'en_link' );
82 var contents = parsed.contents();
83 expect( contents.length ).toEqual( 3 );
84 expect( contents[0].nodeName ).toEqual( '#text' );
85 expect( contents[0].nodeValue ).toEqual( 'Simple ' );
86 expect( contents[1].nodeName ).toEqual( 'A' );
87 expect( contents[1].getAttribute( 'href' ) ).toEqual( 'http://example.com' );
88 expect( contents[1].childNodes[0].nodeValue ).toEqual( 'link to example' );
89 expect( contents[2].nodeName ).toEqual( '#text' );
90 expect( contents[2].nodeValue ).toEqual( '.' );
91 } );
92
93 it ( "should replace a URL into a link", function() {
94 var parser = new mw.jqueryMsg.parser();
95 var parsed = parser.parse( 'en_link_replace', [ 'http://example.com/foo', 'linking' ] );
96 var contents = parsed.contents();
97 expect( contents.length ).toEqual( 3 );
98 expect( contents[0].nodeName ).toEqual( '#text' );
99 expect( contents[0].nodeValue ).toEqual( 'Complex ' );
100 expect( contents[1].nodeName ).toEqual( 'A' );
101 expect( contents[1].getAttribute( 'href' ) ).toEqual( 'http://example.com/foo' );
102 expect( contents[1].childNodes[0].nodeValue ).toEqual( 'linking' );
103 expect( contents[2].nodeName ).toEqual( '#text' );
104 expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
105 } );
106
107 it ( "should bind a click handler into a link", function() {
108 var parser = new mw.jqueryMsg.parser();
109 var clicked = false;
110 var click = function() { clicked = true; };
111 var parsed = parser.parse( 'en_link_replace', [ click, 'linking' ] );
112 var contents = parsed.contents();
113 expect( contents.length ).toEqual( 3 );
114 expect( contents[0].nodeName ).toEqual( '#text' );
115 expect( contents[0].nodeValue ).toEqual( 'Complex ' );
116 expect( contents[1].nodeName ).toEqual( 'A' );
117 expect( contents[1].getAttribute( 'href' ) ).toEqual( '#' );
118 expect( contents[1].childNodes[0].nodeValue ).toEqual( 'linking' );
119 expect( contents[2].nodeName ).toEqual( '#text' );
120 expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
121 // determining bindings is hard in IE
122 var anchor = parsed.find( 'a' );
123 if ( ( $.browser.mozilla || $.browser.webkit ) && anchor.click ) {
124 expect( clicked ).toEqual( false );
125 anchor.click();
126 expect( clicked ).toEqual( true );
127 }
128 } );
129
130 it ( "should wrap a jquery arg around link contents -- even another element", function() {
131 var parser = new mw.jqueryMsg.parser();
132 var clicked = false;
133 var click = function() { clicked = true; };
134 var button = $( '<button>' ).click( click );
135 var parsed = parser.parse( 'en_link_replace', [ button, 'buttoning' ] );
136 var contents = parsed.contents();
137 expect( contents.length ).toEqual( 3 );
138 expect( contents[0].nodeName ).toEqual( '#text' );
139 expect( contents[0].nodeValue ).toEqual( 'Complex ' );
140 expect( contents[1].nodeName ).toEqual( 'BUTTON' );
141 expect( contents[1].childNodes[0].nodeValue ).toEqual( 'buttoning' );
142 expect( contents[2].nodeName ).toEqual( '#text' );
143 expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
144 // determining bindings is hard in IE
145 if ( ( $.browser.mozilla || $.browser.webkit ) && button.click ) {
146 expect( clicked ).toEqual( false );
147 parsed.find( 'button' ).click();
148 expect( clicked ).toEqual( true );
149 }
150 } );
151
152
153 } );
154
155
156 describe( "magic keywords", function() {
157 it( "should substitute magic keywords", function() {
158 var options = {
159 magic: {
160 'alohomora' : 'open'
161 }
162 };
163 var parser = new mw.jqueryMsg.parser( options );
164 expect( parser.parse( 'en_simple_magic' ).html() ).toEqual( 'Simple open message' );
165 } );
166 } );
167
168 describe( "error conditions", function() {
169 it( "should return non-existent key in square brackets", function() {
170 var parser = new mw.jqueryMsg.parser();
171 expect( parser.parse( 'en_does_not_exist' ).html() ).toEqual( '[en_does_not_exist]' );
172 } );
173
174
175 it( "should fail to parse", function() {
176 var parser = new mw.jqueryMsg.parser();
177 expect( function() { parser.parse( 'en_fail' ); } ).toThrow(
178 'Parse error at position 20 in input: This should fail to {{parse'
179 );
180 } );
181 } );
182
183 describe( "empty parameters", function() {
184 it( "should deal with empty parameters", function() {
185 var parser = new mw.jqueryMsg.parser();
186 var ast = parser.getAst( 'en_undelete_empty_param' );
187 expect( parser.parse( 'en_undelete_empty_param', [ 1 ] ).html() ).toEqual( 'Undelete' );
188 expect( parser.parse( 'en_undelete_empty_param', [ 3 ] ).html() ).toEqual( 'Undelete multiple edits' );
189
190 } );
191 } );
192
193 describe( "easy message interface functions", function() {
194 it( "should allow a global that returns strings", function() {
195 var gM = mw.jqueryMsg.getMessageFunction();
196 // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
197 // a surrounding <SPAN> is needed for html() to work right
198 var expectedHtml = $( '<span>Complex <a href="http://example.com/foo">linking</a> behaviour.</span>' ).html();
199 var result = gM( 'en_link_replace', 'http://example.com/foo', 'linking' );
200 expect( typeof result ).toEqual( 'string' );
201 expect( result ).toEqual( expectedHtml );
202 } );
203
204 it( "should allow a jQuery plugin that appends to nodes", function() {
205 $.fn.msg = mw.jqueryMsg.getPlugin();
206 var $div = $( '<div>' ).append( $( '<p>' ).addClass( 'foo' ) );
207 var clicked = false;
208 var $button = $( '<button>' ).click( function() { clicked = true; } );
209 $div.find( '.foo' ).msg( 'en_link_replace', $button, 'buttoning' );
210 // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
211 // a surrounding <SPAN> is needed for html() to work right
212 var expectedHtml = $( '<span>Complex <button>buttoning</button> behaviour.</span>' ).html();
213 var createdHtml = $div.find( '.foo' ).html();
214 // it is hard to test for clicks with IE; also it inserts or removes spaces around nodes when creating HTML tags, depending on their type.
215 // so need to check the strings stripped of spaces.
216 if ( ( $.browser.mozilla || $.browser.webkit ) && $button.click ) {
217 expect( createdHtml ).toEqual( expectedHtml );
218 $div.find( 'button ').click();
219 expect( clicked ).toEqual( true );
220 } else if ( $.browser.ie ) {
221 expect( createdHtml.replace( /\s/, '' ) ).toEqual( expectedHtml.replace( /\s/, '' ) );
222 }
223 delete $.fn.msg;
224 } );
225
226 } );
227
228 // The parser functions can throw errors, but let's not actually blow up for the user -- instead dump the error into the interface so we have
229 // a chance at fixing this
230 describe( "easy message interface functions with graceful failures", function() {
231 it( "should allow a global that returns strings, with graceful failure", function() {
232 var gM = mw.jqueryMsg.getMessageFunction();
233 // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
234 // a surrounding <SPAN> is needed for html() to work right
235 var expectedHtml = $( '<span>en_fail: Parse error at position 20 in input: This should fail to {{parse</span>' ).html();
236 var result = gM( 'en_fail' );
237 expect( typeof result ).toEqual( 'string' );
238 expect( result ).toEqual( expectedHtml );
239 } );
240
241 it( "should allow a global that returns strings, with graceful failure on missing magic words", function() {
242 var gM = mw.jqueryMsg.getMessageFunction();
243 // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
244 // a surrounding <SPAN> is needed for html() to work right
245 var expectedHtml = $( '<span>en_fail_magic: unknown operation "sietname"</span>' ).html();
246 var result = gM( 'en_fail_magic' );
247 expect( typeof result ).toEqual( 'string' );
248 expect( result ).toEqual( expectedHtml );
249 } );
250
251
252 it( "should allow a jQuery plugin, with graceful failure", function() {
253 $.fn.msg = mw.jqueryMsg.getPlugin();
254 var $div = $( '<div>' ).append( $( '<p>' ).addClass( 'foo' ) );
255 $div.find( '.foo' ).msg( 'en_fail' );
256 // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
257 // a surrounding <SPAN> is needed for html() to work right
258 var expectedHtml = $( '<span>en_fail: Parse error at position 20 in input: This should fail to {{parse</span>' ).html();
259 var createdHtml = $div.find( '.foo' ).html();
260 expect( createdHtml ).toEqual( expectedHtml );
261 delete $.fn.msg;
262 } );
263
264 } );
265
266
267
268
269 describe( "test plurals and other language-specific functions", function() {
270 /* copying some language definitions in here -- it's hard to make this test fast and reliable
271 otherwise, and we don't want to have to know the mediawiki URL from this kind of test either.
272 We also can't preload the langs for the test since they clobber the same namespace.
273 In principle Roan said it was okay to change how languages worked so that didn't happen... maybe
274 someday. We'd have to the same kind of importing of the default rules for most rules, or maybe
275 come up with some kind of subclassing scheme for languages */
276 var languageClasses = {
277 ar: {
278 /**
279 * Arabic (العربية) language functions
280 */
281
282 convertPlural: function( count, forms ) {
283 forms = mw.language.preConvertPlural( forms, 6 );
284 if ( count === 0 ) {
285 return forms[0];
286 }
287 if ( count == 1 ) {
288 return forms[1];
289 }
290 if ( count == 2 ) {
291 return forms[2];
292 }
293 if ( count % 100 >= 3 && count % 100 <= 10 ) {
294 return forms[3];
295 }
296 if ( count % 100 >= 11 && count % 100 <= 99 ) {
297 return forms[4];
298 }
299 return forms[5];
300 },
301
302 digitTransformTable: {
303 '0': '٠', // &#x0660;
304 '1': '١', // &#x0661;
305 '2': '٢', // &#x0662;
306 '3': '٣', // &#x0663;
307 '4': '٤', // &#x0664;
308 '5': '٥', // &#x0665;
309 '6': '٦', // &#x0666;
310 '7': '٧', // &#x0667;
311 '8': '٨', // &#x0668;
312 '9': '٩', // &#x0669;
313 '.': '٫', // &#x066b; wrong table ?
314 ',': '٬' // &#x066c;
315 }
316
317 },
318 en: { },
319 fr: {
320 convertPlural: function( count, forms ) {
321 forms = mw.language.preConvertPlural( forms, 2 );
322 return ( count <= 1 ) ? forms[0] : forms[1];
323 }
324 },
325 jp: { },
326 zh: { }
327 };
328
329 /* simulate how the language classes override, or don't, the standard functions in mw.language */
330 $.each( languageClasses, function( langCode, rules ) {
331 $.each( [ 'convertPlural', 'convertNumber' ], function( i, propertyName ) {
332 if ( typeof rules[ propertyName ] === 'undefined' ) {
333 rules[ propertyName ] = mw.language[ propertyName ];
334 }
335 } );
336 } );
337
338 $.each( jasmineMsgSpec, function( i, test ) {
339 it( "should parse " + test.name, function() {
340 // using language override so we don't have to muck with global namespace
341 var parser = new mw.jqueryMsg.parser( { language: languageClasses[ test.lang ] } );
342 var parsedHtml = parser.parse( test.key, test.args ).html();
343 expect( parsedHtml ).toEqual( test.result );
344 } );
345 } );
346
347 } );
348
349 } );
350 } )( window.mediaWiki, jQuery );