diff --git a/tests/test_helpers.py b/tests/test_helpers.py index afadd5f28..e7f160fad 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -15,13 +15,14 @@ import webcompat from webcompat.helpers import format_link_header +from webcompat.helpers import get_browser_name +from webcompat.helpers import get_browser +from webcompat.helpers import get_os from webcompat.helpers import normalize_api_params from webcompat.helpers import parse_link_header from webcompat.helpers import rewrite_and_sanitize_link from webcompat.helpers import rewrite_links from webcompat.helpers import sanitize_link -from webcompat.helpers import get_browser_name -from webcompat.helpers import get_browser ACCESS_TOKEN_LINK = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # nopep8 @@ -30,12 +31,16 @@ REWRITTEN_ISSUES_LINK_HEADER = '; rel="next", ; rel="last", ; rel="first", ; rel="prev"' # nopep8 REWRITTEN_SEARCH_LINK_HEADER = '; rel="next", ; rel="last"' # nopep8 PARSED_LINKED_HEADERS = [{'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=3', 'rel': 'next'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=4', 'rel': 'last'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'first'}, {'link': 'https://api.github.com/repositories/17839063/issues?per_page=50&page=1', 'rel': 'prev'}] # nopep8 -NON_TABLET_UA = "Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0" # nopep8 -TABLET_UA = "Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0" # nopep8 -PARSED_NON_TABLET_BROWSER_NAME = "firefox mobile" -PARSED_TABLET_BROWSER_NAME = "firefox mobile tablet" -PARSED_NON_TABLET_BROWSER = "Firefox Mobile 40.0" -PARSED_TABLET_BROWSER = "Firefox Mobile 41.0 (Tablet)" +FIREFOX_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:48.0) Gecko/20100101 Firefox/48.0' # nopep8 +FIREFOX_MOBILE_UA_OLD = 'Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # nopep8 +FIREFOX_MOBILE_UA = 'Mozilla/5.0 (Android 6.0.1; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0' # nopep8 +FIREFOX_TABLET_UA = 'Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0' # nopep8 +SAFARI_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11) AppleWebKit/601.1.39 (KHTML, like Gecko) Version/9.0 Safari/601.1.39' # nopep8 +SAFARI_MOBILE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25' # nopep8 +SAFARI_TABLET_UA = 'Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3' # nopep8 +CHROME_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2720.0 Safari/537.36' # nopep8 +CHROME_MOBILE_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19' # nopep8 +CHROME_TABLET_UA = 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Safari/535.19' # nopep8 class TestHelpers(unittest.TestCase): @@ -117,29 +122,73 @@ def test_format_http_link_headers(self): link_header = GITHUB_ISSUES_LINK_HEADER self.assertEqual(format_link_header(parsed_headers), link_header) - def test_get_browser_name_Tablet(self): - '''Test Browser name parsing for tablet devices.''' - user_agent = TABLET_UA - parsed_browser_name = PARSED_TABLET_BROWSER_NAME - self.assertEqual(get_browser_name(user_agent), parsed_browser_name) - - def test_get_browser_name_Non_Tablet(self): - '''Test Browser name parsing for non-tablet devices.''' - user_agent = NON_TABLET_UA - parsed_browser_name = PARSED_NON_TABLET_BROWSER_NAME - self.assertEqual(get_browser_name(user_agent), parsed_browser_name) - - def test_get_browser_Tablet(self): - '''Test Browser parsing for tablet devices.''' - user_agent = TABLET_UA - parsed_browser = PARSED_TABLET_BROWSER - self.assertEqual(get_browser(user_agent), parsed_browser) - - def test_get_browser_Non_Tablet(self): - '''Test Browser parsing for non-tablet devices.''' - user_agent = NON_TABLET_UA - parsed_browser = PARSED_NON_TABLET_BROWSER - self.assertEqual(get_browser(user_agent), parsed_browser) + def test_get_browser_name(self): + '''Test browser name parsing via get_browser_name helper method.''' + self.assertEqual(get_browser_name(FIREFOX_UA), 'firefox') + self.assertEqual(get_browser_name(FIREFOX_MOBILE_UA), 'firefox mobile') + self.assertEqual(get_browser_name(FIREFOX_MOBILE_UA_OLD), + 'firefox mobile') + self.assertEqual(get_browser_name(FIREFOX_TABLET_UA), + 'firefox mobile (tablet)') + self.assertEqual(get_browser_name(SAFARI_UA), 'safari') + self.assertEqual(get_browser_name(SAFARI_MOBILE_UA), 'mobile safari') + self.assertEqual(get_browser_name(SAFARI_TABLET_UA), 'mobile safari') + self.assertEqual(get_browser_name(CHROME_UA), 'chrome') + self.assertEqual(get_browser_name(CHROME_MOBILE_UA), 'chrome mobile') + self.assertEqual(get_browser_name(CHROME_TABLET_UA), 'chrome') + self.assertEqual(get_browser_name(''), 'unknown') + self.assertEqual(get_browser_name(None), 'unknown') + self.assertEqual(get_browser_name(), 'unknown') + self.assertEqual(get_browser_name(u'💀'), 'unknown') + self.assertEqual(get_browser(''), 'Unknown') + self.assertEqual(get_browser(True), 'Unknown') + self.assertEqual(get_browser(False), 'Unknown') + self.assertEqual(get_browser(None), 'Unknown') + + def test_get_browser(self): + '''Test browser parsing via get_browser helper method.''' + self.assertEqual(get_browser(FIREFOX_UA), 'Firefox 48.0') + self.assertEqual(get_browser(FIREFOX_MOBILE_UA), 'Firefox Mobile 40.0') + self.assertEqual(get_browser_name(FIREFOX_MOBILE_UA_OLD), + 'firefox mobile') + self.assertEqual(get_browser(FIREFOX_TABLET_UA), + 'Firefox Mobile (Tablet) 41.0') + self.assertEqual(get_browser(SAFARI_UA), 'Safari 9.0') + self.assertEqual(get_browser(SAFARI_MOBILE_UA), 'Mobile Safari 6.0') + self.assertEqual(get_browser(SAFARI_TABLET_UA), 'Mobile Safari 5.1') + self.assertEqual(get_browser(CHROME_UA), 'Chrome 52.0.2720') + self.assertEqual(get_browser(CHROME_MOBILE_UA), + 'Chrome Mobile 18.0.1025') + self.assertEqual(get_browser(CHROME_TABLET_UA), 'Chrome 18.0.1025') + self.assertEqual(get_browser(''), 'Unknown') + self.assertEqual(get_browser(), 'Unknown') + self.assertEqual(get_browser(u'💀'), 'Unknown') + self.assertEqual(get_browser(''), 'Unknown') + self.assertEqual(get_browser(True), 'Unknown') + self.assertEqual(get_browser(False), 'Unknown') + self.assertEqual(get_browser(None), 'Unknown') + + def test_get_os(self): + '''Test OS parsing via get_os helper method.''' + self.assertEqual(get_os(FIREFOX_UA), 'Mac OS X 10.11') + self.assertEqual(get_os(FIREFOX_MOBILE_UA), 'Android 6.0.1') + self.assertEqual(get_os(FIREFOX_MOBILE_UA_OLD), 'Android') + self.assertEqual(get_os(FIREFOX_TABLET_UA), 'Android 4.4') + self.assertEqual(get_os(SAFARI_UA), 'Mac OS X 10.11') + self.assertEqual(get_os(SAFARI_MOBILE_UA), 'iOS 6.1.4') + self.assertEqual(get_os(SAFARI_TABLET_UA), 'iOS 5.1.1') + self.assertEqual(get_os(CHROME_UA), 'Mac OS X 10.11.4') + self.assertEqual(get_os(CHROME_MOBILE_UA), + 'Android 4.0.4') + self.assertEqual(get_os(CHROME_TABLET_UA), 'Android 4.0.4') + self.assertEqual(get_os(''), 'Unknown') + self.assertEqual(get_os(), 'Unknown') + self.assertEqual(get_os(u'💀'), 'Unknown') + self.assertEqual(get_os(''), 'Unknown') + self.assertEqual(get_os(True), 'Unknown') + self.assertEqual(get_os(False), 'Unknown') + self.assertEqual(get_os(None), 'Unknown') + if __name__ == '__main__': unittest.main() diff --git a/webcompat/helpers.py b/webcompat/helpers.py index b3b380a87..62dc015d7 100644 --- a/webcompat/helpers.py +++ b/webcompat/helpers.py @@ -99,61 +99,71 @@ def get_user_info(): session['avatar_url'] = gh_user.get('avatar_url') -def get_browser_name(user_agent_string): - '''Return just the browser name. - - unknown user agents will be reported as "other". - ''' - ua_dict = user_agent_parser.Parse(user_agent_string) - name = ua_dict.get('user_agent').get('family').lower() - device = ua_dict.get('device').get('model') - if (name == 'firefox mobile' and - ua_dict.get('os').get('family') == 'Firefox OS'): - name = 'other' - if device == 'Tablet': - name += " " + device.lower() - return name - - -def get_browser(user_agent_string): +def get_browser(user_agent_string=None): '''Return browser name family and version. It will pre-populate the bug reporting form. ''' - ua_dict = user_agent_parser.Parse(user_agent_string) - ua = ua_dict.get('user_agent') - name = ua.get('family') - version = ua.get('major', u'Unknown') - # Add on the minor and patch version numbers if they exist - if version != u'Unknown' and ua.get('minor'): - version = version + "." + ua.get('minor') - if ua.get('patch'): - version = version + "." + ua.get('patch') - else: - version = '' - # Check for tablet devices - if ua_dict.get('device').get('model') == 'Tablet': - model = ' (Tablet)' - else: - model = '' - return '{0} {1}{2}'.format(name, version, model) + if user_agent_string and isinstance(user_agent_string, basestring): + ua_dict = user_agent_parser.Parse(user_agent_string) + ua = ua_dict.get('user_agent') + name = ua.get('family') + version = ua.get('major', u'Unknown') + # Add on the minor and patch version numbers if they exist + if version != u'Unknown' and ua.get('minor'): + version = version + "." + ua.get('minor') + if ua.get('patch'): + version = version + "." + ua.get('patch') + else: + version = '' + # Check for tablet devices + if ua_dict.get('device').get('model') == 'Tablet': + model = '(Tablet) ' + else: + model = '' + rv = '{0} {1}{2}'.format(name, model, version) + # bizarre UA strings can be parsed like so: + # {'major': None, 'minor': None, 'family': 'Other', 'patch': None} + # but we want to return "Unknown", rather than "Other" + if rv.strip().lower() == "other": + return "Unknown" + return rv + return "Unknown" + + +def get_browser_name(user_agent_string=None): + '''Return just the browser name. + + unknown user agents will be reported as "unknown". + ''' + if user_agent_string and isinstance(user_agent_string, basestring): + # get_browser will return something like 'Chrome Mobile 47.0' + # we just want 'chrome mobile', i.e., the lowercase name + # w/o the version + return get_browser(user_agent_string).rsplit(' ', 1)[0].lower() + return "unknown" -def get_os(user_agent_string): +def get_os(user_agent_string=None): '''Return operating system name. It pre-populates the bug reporting form. ''' - ua_dict = user_agent_parser.Parse(user_agent_string) - os = ua_dict.get('os') - version = os.get('major', u'Unknown') - if version != u'Unknown' and os.get('major'): - version = version + "." + os.get('minor') - if os.get('patch'): - version = version + "." + os.get('patch') - else: - version = '' - return '{0} {1}'.format(os.get('family'), version) + if user_agent_string and isinstance(user_agent_string, basestring): + ua_dict = user_agent_parser.Parse(user_agent_string) + os = ua_dict.get('os') + version = os.get('major', u'Unknown') + if version != u'Unknown' and os.get('minor'): + version = version + "." + os.get('minor') + if os.get('patch'): + version = version + "." + os.get('patch') + else: + version = '' + rv = '{0} {1}'.format(os.get('family'), version).rstrip() + if rv.strip().lower() == "other": + return "Unknown" + return rv + return "Unknown" def get_response_headers(response): diff --git a/webcompat/views.py b/webcompat/views.py index d82536720..017cc400c 100644 --- a/webcompat/views.py +++ b/webcompat/views.py @@ -138,6 +138,7 @@ def index(): ua_header = request.headers.get('User-Agent') bug_form.browser.data = get_browser(ua_header) bug_form.os.data = get_os(ua_header) + # browser_name is used in topbar.html to show the right add-on link browser_name = get_browser_name(ua_header) # GET means you want to file a report. if request.method == 'GET': @@ -151,7 +152,8 @@ def index(): else: # Validation failed, re-render the form with the errors. - return render_template('index.html', form=bug_form) + return render_template('index.html', form=bug_form, + browser=browser_name) @app.route('/issues')