2 var simpleSample
, U_20AC
, poop
, mbSample
;
4 QUnit
.module( 'jquery.lengthLimit', QUnit
.newMwEnvironment() );
6 // Simple sample (20 chars, 20 bytes)
7 simpleSample
= '12345678901234567890';
9 // 3 bytes (euro-symbol)
12 // Outside of the BMP (pile of poo emoji)
13 poop
= '\uD83D\uDCA9'; // "💩"
15 // Multi-byte sample (22 chars, 26 bytes)
16 mbSample
= '1234567890' + U_20AC
+ '1234567890' + U_20AC
;
18 // Basic sendkey-implementation
19 function addChars( $input
, charstr
) {
22 function x( $input
, i
) {
23 // Add character to the value
24 return $input
.val() + charstr
.charAt( i
);
27 for ( c
= 0, len
= charstr
.length
; c
< len
; c
+= 1 ) {
29 .val( x( $input
, c
) )
35 * Test factory for $.fn.byteLimit
37 * @param {Object} options
38 * @param {string} options.description Test name
39 * @param {jQuery} options.$input jQuery object in an input element
40 * @param {string} options.sample Sequence of characters to simulate being
42 * @param {string} options.expected Expected final value of `$input`
44 function byteLimitTest( options
) {
52 QUnit
.test( opt
.description
, function ( assert
) {
53 opt
.$input
.appendTo( '#qunit-fixture' );
55 // Simulate pressing keys for each of the sample characters
56 addChars( opt
.$input
, opt
.sample
);
61 'New value matches the expected string'
67 description
: 'Plain text input',
68 $input
: $( '<input>' ).attr( 'type', 'text' ),
70 expected
: simpleSample
74 description
: 'Plain text input. Calling byteLimit with no parameters and no maxlength attribute (T38310)',
75 $input
: $( '<input>' ).attr( 'type', 'text' )
78 expected
: simpleSample
82 description
: 'Limit using the maxlength attribute',
83 $input
: $( '<input>' ).attr( 'type', 'text' )
84 .attr( 'maxlength', '10' )
87 expected
: '1234567890'
91 description
: 'Limit using a custom value',
92 $input
: $( '<input>' ).attr( 'type', 'text' )
95 expected
: '1234567890'
99 description
: 'Limit using a custom value, overriding maxlength attribute',
100 $input
: $( '<input>' ).attr( 'type', 'text' )
101 .attr( 'maxlength', '10' )
103 sample
: simpleSample
,
104 expected
: '123456789012345'
108 description
: 'Limit using a custom value (multibyte)',
109 $input
: $( '<input>' ).attr( 'type', 'text' )
112 expected
: '1234567890' + U_20AC
+ '1'
116 description
: 'Limit using a custom value (multibyte, outside BMP)',
117 $input
: $( '<input>' ).attr( 'type', 'text' )
124 description
: 'Limit using a custom value (multibyte) overlapping a byte',
125 $input
: $( '<input>' ).attr( 'type', 'text' )
128 expected
: '123456789012'
132 description
: 'Pass the limit and a callback as input filter',
133 $input
: $( '<input>' ).attr( 'type', 'text' )
134 .byteLimit( 6, function ( val
) {
135 var title
= mw
.Title
.newFromText( String( val
) );
136 // Return without namespace prefix
137 return title
? title
.getMain() : '';
139 sample
: 'User:Sample',
140 expected
: 'User:Sample'
144 description
: 'Limit using the maxlength attribute and pass a callback as input filter',
145 $input
: $( '<input>' ).attr( 'type', 'text' )
146 .attr( 'maxlength', '6' )
147 .byteLimit( function ( val
) {
148 var title
= mw
.Title
.newFromText( String( val
) );
149 // Return without namespace prefix
150 return title
? title
.getMain() : '';
152 sample
: 'User:Sample',
153 expected
: 'User:Sample'
157 description
: 'Pass the limit and a callback as input filter',
158 $input
: $( '<input>' ).attr( 'type', 'text' )
159 .byteLimit( 6, function ( val
) {
160 var title
= mw
.Title
.newFromText( String( val
) );
161 // Return without namespace prefix
162 return title
? title
.getMain() : '';
164 sample
: 'User:Example',
165 // The callback alters the value to be used to calculeate
166 // the length. The altered value is "Exampl" which has
167 // a length of 6, the "e" would exceed the limit.
168 expected
: 'User:Exampl'
172 description
: 'Input filter that increases the length',
173 $input
: $( '<input>' ).attr( 'type', 'text' )
174 .byteLimit( 10, function ( text
) {
175 return 'prefix' + text
;
177 sample
: simpleSample
,
178 // Prefix adds 6 characters, limit is reached after 4
182 // Regression tests for T43450
184 description
: 'Input filter of which the base exceeds the limit',
185 $input
: $( '<input>' ).attr( 'type', 'text' )
186 .byteLimit( 3, function ( text
) {
187 return 'prefix' + text
;
189 sample
: simpleSample
,
193 QUnit
.test( 'Confirm properties and attributes set', function ( assert
) {
196 $el
= $( '<input>' ).attr( 'type', 'text' )
197 .attr( 'maxlength', '7' )
198 .appendTo( '#qunit-fixture' )
201 assert
.strictEqual( $el
.attr( 'maxlength' ), '7', 'maxlength attribute unchanged for simple limit' );
203 $el
= $( '<input>' ).attr( 'type', 'text' )
204 .attr( 'maxlength', '7' )
205 .appendTo( '#qunit-fixture' )
208 assert
.strictEqual( $el
.attr( 'maxlength' ), '12', 'maxlength attribute updated for custom limit' );
210 $el
= $( '<input>' ).attr( 'type', 'text' )
211 .attr( 'maxlength', '7' )
212 .appendTo( '#qunit-fixture' )
213 .byteLimit( 12, function ( val
) {
217 assert
.strictEqual( $el
.attr( 'maxlength' ), undefined, 'maxlength attribute removed for limit with callback' );
219 $( '<input>' ).attr( 'type', 'text' )
220 .addClass( 'mw-test-byteLimit-foo' )
221 .attr( 'maxlength', '7' )
222 .appendTo( '#qunit-fixture' );
224 $( '<input>' ).attr( 'type', 'text' )
225 .addClass( 'mw-test-byteLimit-foo' )
226 .attr( 'maxlength', '12' )
227 .appendTo( '#qunit-fixture' );
229 $el
= $( '.mw-test-byteLimit-foo' );
231 assert
.strictEqual( $el
.length
, 2, 'Verify that there are no other elements clashing with this test suite' );
236 QUnit
.test( 'Trim from insertion when limit exceeded', function ( assert
) {
239 // Use a new <input> because the bug only occurs on the first time
240 // the limit it reached (T42850)
241 $el
= $( '<input>' ).attr( 'type', 'text' )
242 .appendTo( '#qunit-fixture' )
244 .val( 'abc' ).trigger( 'change' )
245 .val( 'zabc' ).trigger( 'change' );
247 assert
.strictEqual( $el
.val(), 'abc', 'Trim from the insertion point (at 0), not the end' );
249 $el
= $( '<input>' ).attr( 'type', 'text' )
250 .appendTo( '#qunit-fixture' )
252 .val( 'abc' ).trigger( 'change' )
253 .val( 'azbc' ).trigger( 'change' );
255 assert
.strictEqual( $el
.val(), 'abc', 'Trim from the insertion point (at 1), not the end' );
258 QUnit
.test( 'Do not cut up false matching substrings in emoji insertions', function ( assert
) {
260 oldVal
= '\uD83D\uDCA9\uD83D\uDCA9', // "💩💩"
261 newVal
= '\uD83D\uDCA9\uD83D\uDCB9\uD83E\uDCA9\uD83D\uDCA9', // "💩💹🢩💩"
262 expected
= '\uD83D\uDCA9\uD83D\uDCB9\uD83D\uDCA9'; // "💩💹💩"
264 // Possible bad results:
265 // * With no surrogate support:
266 // '\uD83D\uDCA9\uD83D\uDCB9\uD83E\uDCA9' "💩💹🢩"
267 // * With correct trimming but bad detection of inserted text:
268 // '\uD83D\uDCA9\uD83D\uDCB9\uDCA9' "💩💹�"
270 $el
= $( '<input>' ).attr( 'type', 'text' )
271 .appendTo( '#qunit-fixture' )
273 .val( oldVal
).trigger( 'change' )
274 .val( newVal
).trigger( 'change' );
276 assert
.strictEqual( $el
.val(), expected
, 'Pasted emoji correctly trimmed at the end' );
280 description
: 'Unpaired surrogates do not crash',
281 $input
: $( '<input>' ).attr( 'type', 'text' ).byteLimit( 4 ),
282 sample
: '\uD800\uD800\uDFFF',
286 }( jQuery
, mediaWiki
) );