From b4f0c3ea62fca995f57a6669d786e77894eb295e Mon Sep 17 00:00:00 2001 From: Krinkle Date: Mon, 4 Jul 2011 19:11:08 +0000 Subject: [PATCH] jquery.client unit testing - More elaborate caching in the plugin in order for the code to be testable - Added support for passing custom user agents (although the default behavior remains unchanged) - Added support for passing custom profile-objects to $.client.test - Added test case for all supported browsers according to http://www.mediawiki.org/wiki/Compatibility#Browser - Replaced userAgent.toLowerCase() with userAgent since this string was already converted to lower case a few lines up This will likely prevent bugs like bug 27652 and bug 29446. --- resources/jquery/jquery.client.js | 51 +++--- .../suites/resources/jquery/jquery.client.js | 149 +++++++++++++++++- 2 files changed, 179 insertions(+), 21 deletions(-) diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js index 74a2e52311..8082fa7d20 100644 --- a/resources/jquery/jquery.client.js +++ b/resources/jquery/jquery.client.js @@ -5,16 +5,22 @@ /* Private Members */ - var profile; + /** + * @var profileCache {Object} Keyed by userAgent string, + * value is the parsed $.client.profile object for that user agent. + */ + var profileCache = {}; /* Public Methods */ $.client = { /** - * Returns an object containing information about the browser + * Get an object containing information about the client. * - * The resulting client object will be in the following format: + * @param nav {Object} An object with atleast a 'userAgent' and 'platform' key.= + * Defaults to the global Navigator object. + * @return {Object} The resulting client object will be in the following format: * { * 'name': 'firefox', * 'layout': 'gecko', @@ -25,9 +31,12 @@ * 'versionNumber': 3.5, * } */ - profile: function() { + profile: function( nav ) { + if ( nav === undefined ) { + nav = window.navigator; + } // Use the cached version if possible - if ( profile === undefined ) { + if ( profileCache[nav.userAgent] === undefined ) { /* Configuration */ @@ -90,7 +99,7 @@ /* Pre-processing */ - var userAgent = navigator.userAgent, + var ua = nav.userAgent, match, name = uk, layout = uk, @@ -98,28 +107,28 @@ platform = uk, version = x; - if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( userAgent ) ) { + if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) { // Takes a userAgent string and translates given text into something we can more easily work with - userAgent = translate( userAgent, userAgentTranslations ); + ua = translate( ua, userAgentTranslations ); } // Everything will be in lowercase from now on - userAgent = userAgent.toLowerCase(); + ua = ua.toLowerCase(); /* Extraction */ - if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( userAgent ) ) { + if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) { name = translate( match[1], nameTranslations ); } - if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( userAgent ) ) { + if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) { layout = translate( match[1], layoutTranslations ); } - if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\\/(\\d+)').exec( navigator.userAgent.toLowerCase() ) ) { + if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\\/(\\d+)').exec( ua ) ) { layoutversion = parseInt( match[2], 10 ); } - if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( navigator.platform.toLowerCase() ) ) { + if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) { platform = translate( match[1], platformTranslations ); } - if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( userAgent ) ) { + if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) { version = match[3]; } @@ -131,13 +140,13 @@ } // Expose Opera 10's lies about being Opera 9.8 if ( name === 'opera' && version >= 9.8) { - version = userAgent.match( /version\/([0-9\.]*)/i )[1] || 10; + version = ua.match( /version\/([0-9\.]*)/i )[1] || 10; } var versionNumber = parseFloat( version, 10 ) || 0.0; /* Caching */ - profile = { + profileCache[nav.userAgent] = { 'name': name, 'layout': layout, 'layoutVersion': layoutversion, @@ -147,7 +156,7 @@ 'versionNumber': versionNumber }; } - return profile; + return profileCache[nav.userAgent]; }, /** @@ -171,12 +180,14 @@ * } * } * - * @param map Object of browser support map + * @param map {Object} Browser support map + * @param profile {Object} (optional) a client-profile object. * * @return Boolean true if browser known or assumed to be supported, false if blacklisted */ - test: function( map ) { - var profile = $.client.profile(); + test: function( map, profile ) { + profile = $.isPlainObject( profile ) ? profile : $.client.profile(); + var dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'; // Check over each browser condition to determine if we are running in a compatible client if ( typeof map[dir] !== 'object' || typeof map[dir][profile.name] === 'undefined' ) { diff --git a/tests/qunit/suites/resources/jquery/jquery.client.js b/tests/qunit/suites/resources/jquery/jquery.client.js index 56d1aec05d..50df2928cd 100644 --- a/tests/qunit/suites/resources/jquery/jquery.client.js +++ b/tests/qunit/suites/resources/jquery/jquery.client.js @@ -5,7 +5,154 @@ test( '-- Initial check', function() { ok( jQuery.client, 'jQuery.client defined' ); }); -test( 'profile', function() { +test( 'profile userAgent support', function() { + expect(8); + + // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value) + // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/ + var uas = { + // Internet Explorer 6 + // Internet Explorer 7 + 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': { + title: 'Internet Explorer 7', + platform: 'Win32', + profile: { + "name": "msie", + "layout": "trident", + "layoutVersion": "unknown", + "platform": "win", + "version": "7.0", + "versionBase": "7", + "versionNumber": 7 + } + }, + // Internet Explorer 8 + // Internet Explorer 9 + // Internet Explorer 10 + // Firefox 2 + // Firefox 3.5 + 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': { + title: 'Firefox 3.5', + platform: 'MacIntel', + profile: { + "name": "firefox", + "layout": "gecko", + "layoutVersion": 20110420, + "platform": "mac", + "version": "3.5.19", + "versionBase": "3", + "versionNumber": 3.5 + } + }, + // Firefox 3.6 + 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': { + title: 'Firefox 3.6', + platform: 'Linux i686', + profile: { + "name": "firefox", + "layout": "gecko", + "layoutVersion": 20110422, + "platform": "linux", + "version": "3.6.17", + "versionBase": "3", + "versionNumber": 3.6 + } + }, + // Firefox 4 + 'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': { + title: 'Firefox 4', + platform: 'Win32', + profile: { + "name": "firefox", + "layout": "gecko", + "layoutVersion": 20100101, + "platform": "win", + "version": "4.0.1", + "versionBase": "4", + "versionNumber": 4 + } + }, + // Firefox 5 + // Safari 3 + // Safari 4 + 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { + title: 'Safari 4', + platform: 'MacIntel', + profile: { + "name": "safari", + "layout": "webkit", + "layoutVersion": 531, + "platform": "mac", + "version": "4.0.5", + "versionBase": "4", + "versionNumber": 4 + } + }, + 'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': { + title: 'Safari 4', + platform: 'Win32', + profile: { + "name": "safari", + "layout": "webkit", + "layoutVersion": 533, + "platform": "win", + "version": "4.0.5", + "versionBase": "4", + "versionNumber": 4 + } + }, + // Safari 5 + // Opera 10 + // Chrome 5 + // Chrome 6 + // Chrome 7 + // Chrome 8 + // Chrome 9 + // Chrome 10 + // Chrome 11 + // Chrome 12 + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': { + title: 'Chrome 12', + platform: 'MacIntel', + profile: { + "name": "chrome", + "layout": "webkit", + "layoutVersion": 534, + "platform": "mac", + "version": "12.0.742.112", + "versionBase": "12", + "versionNumber": 12 + } + }, + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': { + title: 'Chrome 12', + platform: 'Linux i686', + profile: { + "name": "chrome", + "layout": "webkit", + "layoutVersion": 534, + "platform": "linux", + "version": "12.0.742.68", + "versionBase": "12", + "versionNumber": 12 + } + } + }; + + // Generate a client profile object and compare recursively + var uaTest = function( rawUserAgent, data ) { + var ret = $.client.profile( { + userAgent: rawUserAgent, + platform: data.platform + } ); + deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent ); + }; + + // Loop through and run tests + $.each( uas, uaTest ); +} ); + +test( 'profile return validation for current user agent', function() { expect(7); var p = $.client.profile(); var unknownOrType = function( val, type, summary ) { -- 2.20.1