jquery.byteLimit: Fix infinite loop if text is longer than max
[lhc/web/wiklou.git] / tests / qunit / suites / resources / jquery / jquery.byteLimit.test.js
1 ( function ( $, mw ) {
2 var simpleSample, U_20AC, mbSample;
3
4 QUnit.module( 'jquery.byteLimit', QUnit.newMwEnvironment() );
5
6 // Simple sample (20 chars, 20 bytes)
7 simpleSample = '12345678901234567890';
8
9 // 3 bytes (euro-symbol)
10 U_20AC = '\u20AC';
11
12 // Multi-byte sample (22 chars, 26 bytes)
13 mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
14
15 // Basic sendkey-implementation
16 function addChars( $input, charstr ) {
17 var c, len;
18
19 function x( $input, i ) {
20 // Add character to the value
21 return $input.val() + charstr.charAt( i );
22 }
23
24 for ( c = 0, len = charstr.length; c < len; c += 1 ) {
25 $input
26 .val( x( $input, c ) )
27 .trigger( 'change' );
28 }
29 }
30
31 /**
32 * Test factory for $.fn.byteLimit
33 *
34 * @param {Object} options
35 * @param {string} options.description Test name
36 * @param {jQuery} options.$input jQuery object in an input element
37 * @param {string} options.sample Sequence of characters to simulate being
38 * added one by one
39 * @param {string} options.expected Expected final value of `$input`
40 */
41 function byteLimitTest( options ) {
42 var opt = $.extend( {
43 description: '',
44 $input: null,
45 sample: '',
46 expected: ''
47 }, options );
48
49 QUnit.asyncTest( opt.description, 1, function ( assert ) {
50 setTimeout( function () {
51 opt.$input.appendTo( '#qunit-fixture' );
52
53 // Simulate pressing keys for each of the sample characters
54 addChars( opt.$input, opt.sample );
55
56 assert.equal(
57 opt.$input.val(),
58 opt.expected,
59 'New value matches the expected string'
60 );
61
62 QUnit.start();
63 }, 10 );
64 } );
65 }
66
67 byteLimitTest( {
68 description: 'Plain text input',
69 $input: $( '<input type="text"/>' ),
70 sample: simpleSample,
71 expected: simpleSample
72 } );
73
74 byteLimitTest( {
75 description: 'Plain text input. Calling byteLimit with no parameters and no maxlength attribute (bug 36310)',
76 $input: $( '<input type="text"/>' )
77 .byteLimit(),
78 sample: simpleSample,
79 expected: simpleSample
80 } );
81
82 byteLimitTest( {
83 description: 'Limit using the maxlength attribute',
84 $input: $( '<input type="text"/>' )
85 .attr( 'maxlength', '10' )
86 .byteLimit(),
87 sample: simpleSample,
88 expected: '1234567890'
89 } );
90
91 byteLimitTest( {
92 description: 'Limit using a custom value',
93 $input: $( '<input type="text"/>' )
94 .byteLimit( 10 ),
95 sample: simpleSample,
96 expected: '1234567890'
97 } );
98
99 byteLimitTest( {
100 description: 'Limit using a custom value, overriding maxlength attribute',
101 $input: $( '<input type="text"/>' )
102 .attr( 'maxlength', '10' )
103 .byteLimit( 15 ),
104 sample: simpleSample,
105 expected: '123456789012345'
106 } );
107
108 byteLimitTest( {
109 description: 'Limit using a custom value (multibyte)',
110 $input: $( '<input type="text"/>' )
111 .byteLimit( 14 ),
112 sample: mbSample,
113 expected: '1234567890' + U_20AC + '1'
114 } );
115
116 byteLimitTest( {
117 description: 'Limit using a custom value (multibyte) overlapping a byte',
118 $input: $( '<input type="text"/>' )
119 .byteLimit( 12 ),
120 sample: mbSample,
121 expected: '1234567890' + '12'
122 } );
123
124 byteLimitTest( {
125 description: 'Pass the limit and a callback as input filter',
126 $input: $( '<input type="text"/>' )
127 .byteLimit( 6, function ( val ) {
128 // Invalid title
129 if ( val === '' ) {
130 return '';
131 }
132
133 // Return without namespace prefix
134 return new mw.Title( String( val ) ).getMain();
135 } ),
136 sample: 'User:Sample',
137 expected: 'User:Sample'
138 } );
139
140 byteLimitTest( {
141 description: 'Limit using the maxlength attribute and pass a callback as input filter',
142 $input: $( '<input type="text"/>' )
143 .attr( 'maxlength', '6' )
144 .byteLimit( function ( val ) {
145 // Invalid title
146 if ( val === '' ) {
147 return '';
148 }
149
150 // Return without namespace prefix
151 return new mw.Title( String( val ) ).getMain();
152 } ),
153 sample: 'User:Sample',
154 expected: 'User:Sample'
155 } );
156
157 byteLimitTest( {
158 description: 'Pass the limit and a callback as input filter',
159 $input: $( '<input type="text"/>' )
160 .byteLimit( 6, function ( val ) {
161 // Invalid title
162 if ( val === '' ) {
163 return '';
164 }
165
166 // Return without namespace prefix
167 return new mw.Title( String( val ) ).getMain();
168 } ),
169 sample: 'User:Example',
170 // The callback alters the value to be used to calculeate
171 // the length. The altered value is "Exampl" which has
172 // a length of 6, the "e" would exceed the limit.
173 expected: 'User:Exampl'
174 } );
175
176 byteLimitTest( {
177 description: 'Input filter that increases the length',
178 $input: $( '<input type="text"/>' )
179 .byteLimit( 10, function ( text ) {
180 return 'prefix' + text;
181 } ),
182 sample: simpleSample,
183 // Prefix adds 6 characters, limit is reached after 4
184 expected: '1234'
185 } );
186
187 // Regression tests for bug 41450
188 byteLimitTest( {
189 description: 'Input filter of which the base exceeds the limit',
190 $input: $( '<input type="text"/>' )
191 .byteLimit( 3, function ( text ) {
192 return 'prefix' + text;
193 } ),
194 sample: simpleSample,
195 hasLimit: true,
196 limit: 6, // 'prefix' length
197 expected: ''
198 } );
199
200 QUnit.test( 'Confirm properties and attributes set', 4, function ( assert ) {
201 var $el, $elA, $elB;
202
203 $el = $( '<input type="text"/>' )
204 .attr( 'maxlength', '7' )
205 .appendTo( '#qunit-fixture' )
206 .byteLimit();
207
208 assert.strictEqual( $el.attr( 'maxlength' ), '7', 'maxlength attribute unchanged for simple limit' );
209
210 $el = $( '<input type="text"/>' )
211 .attr( 'maxlength', '7' )
212 .appendTo( '#qunit-fixture' )
213 .byteLimit( 12 );
214
215 assert.strictEqual( $el.attr( 'maxlength' ), '12', 'maxlength attribute updated for custom limit' );
216
217 $el = $( '<input type="text"/>' )
218 .attr( 'maxlength', '7' )
219 .appendTo( '#qunit-fixture' )
220 .byteLimit( 12, function ( val ) {
221 return val;
222 } );
223
224 assert.strictEqual( $el.attr( 'maxlength' ), undefined, 'maxlength attribute removed for limit with callback' );
225
226 $elA = $( '<input type="text"/>' )
227 .addClass( 'mw-test-byteLimit-foo' )
228 .attr( 'maxlength', '7' )
229 .appendTo( '#qunit-fixture' );
230
231 $elB = $( '<input type="text"/>' )
232 .addClass( 'mw-test-byteLimit-foo' )
233 .attr( 'maxlength', '12' )
234 .appendTo( '#qunit-fixture' );
235
236 $el = $( '.mw-test-byteLimit-foo' );
237
238 assert.strictEqual( $el.length, 2, 'Verify that there are no other elements clashing with this test suite' );
239
240 $el.byteLimit();
241 } );
242
243 QUnit.test( 'Trim from insertion when limit exceeded', 2, function ( assert ) {
244 var $el;
245
246 // Use a new <input /> because the bug only occurs on the first time
247 // the limit it reached (bug 40850)
248 $el = $( '<input type="text"/>' )
249 .appendTo( '#qunit-fixture' )
250 .byteLimit( 3 )
251 .val( 'abc' ).trigger( 'change' )
252 .val( 'zabc' ).trigger( 'change' );
253
254 assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 0), not the end' );
255
256 $el = $( '<input type="text"/>' )
257 .appendTo( '#qunit-fixture' )
258 .byteLimit( 3 )
259 .val( 'abc' ).trigger( 'change' )
260 .val( 'azbc' ).trigger( 'change' );
261
262 assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 1), not the end' );
263 } );
264 }( jQuery, mediaWiki ) );