r109677 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r109676‎ | r109677 | r109678 >
Date:06:21, 21 January 2012
Author:krinkle
Status:ok (Comments)
Tags:
Comment:
[WebFonts.tests] Fixes

* Variables isFontFaceLoaded and fontFamilyList are lacking a "var" statement, were leaking to global scope. Adding local closure and turning them into (local) function expressions and moved to the top of the scope for clarity.

* Removing redundant tests:
** ok( $inputElement.remove() )
** ok( $( 'body' ).append( .. ) )
-- jQuery collections always return a jQuery object, they are chainable prototypes without a status-related return value. These checks don't do anything, they will always return a jQuery object and all objects case to true in JavaScript

* Removing useless tests:
** assertTrue( $selectElement !== [] )
** In JavaScript all objects are compared by reference, this comparison can never succeed because 1) jQuery objects are not arrays, 2) even the, "[] !== []" because they are both separate instances of Array. Arrays and objects must be iterated in order to compare them by value, this is done by deepEqual.

* Fixing element selection.
-- Instead of:
$('body').append( '<input class="foo"/>' );
var $foo = $('input.foo');
-- Use:
var $foo = $( '<input class="foo"/>' );
$('body').append( $foo );
-- No need to query the DOM for this.

* Using equal() instead of ok() as much as possible. ok() is not useful in most unit testing because it makes you have to do the comparison inline. Which means the value given to ok() is only the end result, not the expected/result values themselves. Which means in case of a failure, ok() can only say "Passed" or "Failed", whereas equal() (having both values), can output useful debug information. This is especially important when working with test reports generated externally (eg. through TestSwarm) in which are the test report is all you have.

// Not very useful, the comparison is done inline and ok()
// can only know about the result of the comparison
var x = 'foo';
ok( x === 'bar' ); // Failed.
ok( x === 'foo' ); // Passed.


// Useful! The unit test is give both values so it can also mention
// both in the output:
var x = 'foo';
equal( x, 'bar' ); // Failed. Expected: "bar". Result: "foo"
equal( x, 'foo' ); // Passed. Expected: "foo".
Modified paths:
  • /trunk/extensions/WebFonts/tests/qunit/ext.webfonts.tests.js (modified) (history)

Diff [purge]

Index: trunk/extensions/WebFonts/tests/qunit/ext.webfonts.tests.js
@@ -6,9 +6,51 @@
77 * @copyright Copyright © 2012 Santhosh Thottingal, Amir E. Aharoni
88 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
99 */
 10+( function () {
1011
1112 module( 'ext.webfonts', QUnit.newMwEnvironment() );
1213
 14+function isFontFaceLoaded( fontFamilyName ) {
 15+ var lastStyleIndex, styleIndex, lastStyleSheet, cssText;
 16+
 17+ lastStyleIndex = document.styleSheets.length - 1;
 18+
 19+ // Iterate from last.
 20+ for ( styleIndex = lastStyleIndex; styleIndex > 0; styleIndex-- ) {
 21+ lastStyleSheet = document.styleSheets[styleIndex];
 22+ if ( !lastStyleSheet ) {
 23+ continue;
 24+ }
 25+ if ( !lastStyleSheet.cssRules[0] ) {
 26+ continue;
 27+ }
 28+ cssText = lastStyleSheet.cssRules[0].cssText;
 29+ if ( cssText.indexOf( '@font-face' ) >= 0 && cssText.indexOf( fontFamilyName ) >= 0 ) {
 30+ return true;
 31+ }
 32+ }
 33+
 34+ return false;
 35+}
 36+
 37+// Convert a font-family string to an array. This is needed
 38+// because browsers change the string by adding or removing spaces,
 39+// so the string cannot be compared in a uniform way.
 40+function fontFamilyList( fontFamilyString ) {
 41+ var fontList, fontIndex;
 42+
 43+ // Create a list
 44+ fontList = fontFamilyString.split( /, */ );
 45+
 46+ // Remove the quotes from font names
 47+ for ( fontIndex = 0; fontIndex < fontList.length; ++fontIndex ) {
 48+ fontList[fontIndex] = fontList[fontIndex].replace( /^["']/, '' );
 49+ fontList[fontIndex] = fontList[fontIndex].replace( /["']$/, '' );
 50+ }
 51+
 52+ return fontList;
 53+}
 54+
1355 test( '-- Initial check', function() {
1456 expect(1);
1557
@@ -27,10 +69,10 @@
2870 return;
2971 }
3072
31 - expect( 27 );
 73+ expect( 18 );
3274
3375 var invalidFont = 'NonExistingFont';
34 - assertTrue( mw.webfonts.set( invalidFont ) === undefined, 'A non-existent font is not initialized' );
 76+ strictEqual( mw.webfonts.set( invalidFont ), undefined, 'A non-existent font is not initialized' );
3577 // TODO: test that the right thing was written to the log
3678
3779 var $body = $( 'body' );
@@ -42,17 +84,12 @@
4385 var teluguFont = mw.webfonts.config.languages.te[0];
4486 $body.attr( 'lang', 'te' );
4587
46 - ok( $( 'body' ).append( "<input class='webfonts-testing-element'>input content</input>"), 'An input element for testing was appended to body' );
47 - var $inputElement = $( 'input.webfonts-testing-element' );
48 - assertTrue( $inputElement !== [], 'The input test element is defined' );
49 - ok( $( 'body' ).append( "<select class='webfonts-testing-element'>select content</select>"), 'A select element for testing was appended to body' );
50 - var $selectElement = $( 'select.webfonts-testing-element' );
51 - assertTrue( $selectElement !== [], 'The select test element is defined' );
52 - ok( $( 'body' ).append( "<textarea class='webfonts-testing-element'>textarea content</textarea>"), 'A textarea element for testing was appended to body' );
53 - var $textareaElement = $( 'textarea.webfonts-testing-element' );
54 - assertTrue( $textareaElement !== [], 'The textarea test element is defined' );
 88+ var $inputElement = $( '<input value="input content"/>' );
 89+ var $selectElement = $( '<select><option value="foobar">Foobar</option></select>' );
 90+ var $textareaElement = $( '<textarea>textarea content</textarea>' );
 91+ $( '#qunit-fixture' ).append( $inputElement, $selectElement, $textareaElement );
5592
56 - ok( mw.webfonts.set( teluguFont ), 'Attempted to load a Telugu font for the whole page' );
 93+ assertTrue( mw.webfonts.set( teluguFont ), 'Attempted to load a Telugu font for the whole page' );
5794 var fallbackFonts = 'Helvetica, Arial, sans-serif';
5895 deepEqual( oldConfig, mw.webfonts.oldconfig, 'Previous body css was saved properly' );
5996
@@ -71,7 +108,7 @@
72109 equals( $.cookie( 'webfonts-font' ), teluguFont, 'Correct cookie for the font was set' );
73110
74111 // Reset everything
75 - ok( mw.webfonts.set( false ) === undefined, 'Reset body after testing font application' );
 112+ strictEqual( mw.webfonts.set( false ), undefined, 'Reset body after testing font application' );
76113 equals( $body.css( 'font-family' ), oldConfig.fontFamily, 'Previous font-family for body was restored' );
77114 equals( $body.css( 'font-size' ), oldConfig.fontSize, 'Previous font-size for body was restored' );
78115 equals( $inputElement.css( 'font-family' ), oldConfig.fontFamily, 'Previous font-family for body was restored' );
@@ -81,10 +118,6 @@
82119 equals( $textareaElement.css( 'font-family' ), oldConfig.fontFamily, 'Previous font-family for the textarea element was restored' );
83120 equals( $textareaElement.css( 'font-size' ), oldConfig.fontSize, 'Previous font-size for the textarea element was restored' );
84121
85 - ok( $inputElement.remove(), 'The input test element was removed from body' );
86 - ok( $selectElement.remove(), 'The select test element was removed from body' );
87 - ok( $textareaElement.remove(), 'The textarea test element was removed from body' );
88 -
89122 // Cookie set
90123 equals( $.cookie( 'webfonts-font' ), 'none', 'The cookie was removed' );
91124
@@ -116,7 +149,7 @@
117150 return;
118151 }
119152
120 - expect( 15 );
 153+ expect( 12 );
121154
122155 mw.webfonts.fonts = [];
123156 mw.config.set( {
@@ -126,9 +159,8 @@
127160 wgPageContentLanguage: "en"
128161 } );
129162
130 - ok( $( 'body' ).append( "<p class='webfonts-testing-lang-attr'>Some content</p>"), 'An element for testing lang-based loading was appended to body' );
131 - var $testElement = $( 'p.webfonts-testing-lang-attr' );
132 - assertTrue( $testElement !== [], 'The test element is defined' );
 163+ var $testElement = $( '<p>Some content</p>' );
 164+ $( '#qunit-fixture' ).append( $testElement );
133165
134166 ok( mw.webfonts.loadFontsForLangAttr(), 'Attempted to load fonts for the lang attribute' );
135167 assertFalse( $testElement.hasClass( 'webfonts-lang-attr' ), 'The element has no webfonts-lang-attr class since there is no lang attribute' );
@@ -146,9 +178,6 @@
147179
148180 ok( mw.webfonts.reset(), 'Reset webfonts after testing application by lang' );
149181 assertFalse( $testElement.hasClass( 'webfonts-lang-attr' ), 'The testing element has no webfonts-lang-attr since we reset it' );
150 - // equals( $( 'body' ).find( '*[lang]' ).length, 0, 'There are no elements with the webfonts-lang-attr class' );
151 -
152 - ok( $testElement.remove(), 'The test element was removed from body' );
153182 } );
154183
155184 test( '-- Dynamic font loading based on font-family style attribute', function() {
@@ -156,23 +185,23 @@
157186 return;
158187 }
159188
160 - expect( 14 );
 189+ expect( 11 );
161190
162191 mw.webfonts.fonts = [];
163 - ok( $( 'body' ).append( "<p class='webfonts-testing-font-family-style'>Some content</p>" ), 'An element for testing font-family loading was appended to body' );
164 - var $testElement = $( 'p.webfonts-testing-font-family-style' );
165 - assertTrue( $testElement !== [], 'The test element is defined' );
166192
 193+ var $testElement = $( '<p>Some content</p>' );
 194+ $( '#qunit-fixture' ).append( $testElement );
 195+
167196 var latinWebFont = 'RufScript';
168197 var fallbackFonts = 'Helvetica, Arial, sans-serif';
169 - $testElement.attr( 'style','font-family: ' + latinWebFont + ', ' + fallbackFonts );
 198+ $testElement.attr( 'style', 'font-family: ' + latinWebFont + ', ' + fallbackFonts );
170199 assertTrue( $.inArray( latinWebFont, mw.webfonts.fonts ) === -1, 'Latin font not loaded yet' );
171200 ok( mw.webfonts.loadFontsForFontFamilyStyle(), 'Loaded fonts from font-family' );
172201 assertTrue( $.inArray( latinWebFont, mw.webfonts.fonts ) >= 0, 'Latin font loaded' );
173202 assertTrue( isFontFaceLoaded( latinWebFont ), 'New css rule added to the document for Latin' );
174203
175204 var invalidFont = 'NonExistingFont';
176 - $testElement.attr( 'style','font-family: ' + invalidFont + ', ' + fallbackFonts );
 205+ $testElement.attr( 'style', 'font-family: ' + invalidFont + ', ' + fallbackFonts );
177206 ok( mw.webfonts.loadFontsForFontFamilyStyle(), 'Attempted to load non-existing fonts specified in font-family' );
178207 assertTrue( $.inArray( invalidFont, mw.webfonts.fonts ) === -1, 'Font not loaded since it is not existing, including fallback fonts' );
179208 assertFalse( isFontFaceLoaded( invalidFont ), 'No new css rule added to the document since the font does not exist' );
@@ -183,8 +212,6 @@
184213 ok( mw.webfonts.loadFontsForFontFamilyStyle(), 'Loading fonts from font-family' );
185214 assertTrue( $.inArray( malayalamFont, mw.webfonts.fonts ) >= 0, 'A fallback font was loaded' );
186215 assertTrue( isFontFaceLoaded( malayalamFont ), 'New css rule added to the document for fallback font' );
187 -
188 - ok( $testElement.remove() );
189216 } );
190217
191218 test( '-- Build the menu', function() {
@@ -212,37 +239,4 @@
213240 }
214241 } );
215242
216 -// Tests end here
217 -
218 -isFontFaceLoaded = function( fontFamilyName ) {
219 - var lastStyleIndex = document.styleSheets.length - 1;
220 -
221 - // Iterate from last.
222 - for( var styleIndex = lastStyleIndex; styleIndex > 0; styleIndex-- ) {
223 - var lastStyleSheet = document.styleSheets[styleIndex];
224 - if ( !lastStyleSheet ) { continue; }
225 - if ( !lastStyleSheet.cssRules[0] ) { continue; }
226 - var cssText = lastStyleSheet.cssRules[0].cssText;
227 - if ( cssText.indexOf( '@font-face' ) >= 0 && cssText.indexOf( fontFamilyName ) >= 0 ) {
228 - return true;
229 - }
230 - }
231 -
232 - return false;
233 -};
234 -
235 -// Convert a font-family string to an array. This is needed
236 -// because browsers change the string by adding or removing spaces,
237 -// so the string cannot be compared in a uniform way.
238 -fontFamilyList = function( fontFamilyString ) {
239 - // Create a list
240 - var fontList = fontFamilyString.split( /, */ );
241 -
242 - // Remove the quotes from font names
243 - for ( var fontIndex = 0; fontIndex < fontList.length; ++fontIndex) {
244 - fontList[fontIndex] = fontList[fontIndex].replace( /^["']/, '' );
245 - fontList[fontIndex] = fontList[fontIndex].replace( /["']$/, '' );
246 - }
247 -
248 - return fontList;
249 -};
 243+}());

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r109138More test cases for webfonts...santhosh12:14, 17 January 2012
r109144Modify directly the style attr, .css didn't work - more tests passing now...nikerabbit13:51, 17 January 2012
r109157Better way to check whether @font-face rule is added to the document for the ...santhosh16:11, 17 January 2012
r109545Using the browser checking function from mw.webfonts. Converting the font-fam...amire8016:30, 19 January 2012

Comments

#Comment by Krinkle (talk | contribs)   06:31, 21 January 2012

Forgot to include in commit message:

  • Didn't introduce an alternative for the tests of the element appending. jQuery element creation and appending can be taken for granted at this level. If $foo.append( '<input/>' ); has to be tested, there are way bigger problems to be concerned about.
  • Fixed loose string "input content" that was left inside the ‎<body>. The following line fixed it:
-	ok( $( 'body' ).append( "<input class='webfonts-testing-element'>input content</input>"), 'An input element for testing was appended to body' );
-	var $inputElement =  $( 'input.webfonts-testing-element' );
+	var $inputElement = $( '<input value="input content"/>' );

Because this:

$("<input class='webfonts-testing-element'>input content</input>");

actually creates 2 nodes, not one. Since ‎<input> is a shorttag and cannot hold any content (only attributes), the browser's native parsers turns that into 2 nodes:
<input class="webfonts-testing-element" /> and a Textnode: "input content". And in older version of IE6 invalid HTML like this can also cause an exception to be thrown, when fed to the innerHTML parser.

#Comment by Santhosh.thottingal (talk | contribs)   12:32, 21 January 2012

Thanks a lot Krinkle!

Status & tagging log