2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
7 This module is a simple qa tool that installs extensions and tests whether the
8 browser crashes while visiting a list of urls.
10 Usage: python extensions.py -v
12 Note: This assumes that there is a directory of extensions called
13 'extensions-tool' and that there is a file of newline-separated urls to visit
14 called 'urls.txt' in the data directory.
22 import pyauto_functional
# must be imported before pyauto
26 class ExtensionsPage(object):
27 """Access options in extensions page (chrome://extensions-frame)."""
29 _URL
= 'chrome://extensions-frame'
31 def __init__(self
, driver
):
33 self
._driver
.get(ExtensionsPage
._URL
)
35 def CheckExtensionVisible(self
, ext_id
):
36 """Returns True if |ext_id| exists on page."""
37 return len(self
._driver
.find_elements_by_id(ext_id
)) == 1
39 def SetEnabled(self
, ext_id
, enabled
):
40 """Clicks on 'Enabled' checkbox for specified extension.
43 ext_id: Extension ID to be enabled or disabled.
44 enabled: Boolean indicating whether |ext_id| is to be enabled or disabled.
46 checkbox
= self
._driver
.find_element_by_xpath(
47 '//*[@id="%s"]//*[@class="enable-controls"]//*[@type="checkbox"]' %
49 if checkbox
!= enabled
:
51 # Reload page to ensure that the UI is recreated.
52 self
._driver
.get(ExtensionsPage
._URL
)
54 def SetAllowInIncognito(self
, ext_id
, allowed
):
55 """Clicks on 'Allow in incognito' checkbox for specified extension.
58 ext_id: Extension ID to be enabled or disabled.
59 allowed: Boolean indicating whether |ext_id| is to be allowed or
60 disallowed in incognito.
62 checkbox
= self
._driver
.find_element_by_xpath(
63 '//*[@id="%s"]//*[@class="incognito-control"]//*[@type="checkbox"]' %
65 if checkbox
.is_selected() != allowed
:
67 # Reload page to ensure that the UI is recreated.
68 self
._driver
.get(ExtensionsPage
._URL
)
70 def SetAllowAccessFileURLs(self
, ext_id
, allowed
):
71 """Clicks on 'Allow access to file URLs' checkbox for specified extension.
74 ext_id: Extension ID to be enabled or disabled.
75 allowed: Boolean indicating whether |ext_id| is to be allowed access to
78 checkbox
= self
._driver
.find_element_by_xpath(
79 '//*[@id="%s"]//*[@class="file-access-control"]//*[@type="checkbox"]' %
81 if checkbox
.is_selected() != allowed
:
85 class ExtensionsTest(pyauto
.PyUITest
):
86 """Test of extensions."""
89 """Test method for experimentation.
91 This method is not run automatically.
94 raw_input('Interact with the browser and hit <enter> to dump history.')
96 self
.pprint(self
.GetExtensionsInfo())
98 def _GetInstalledExtensionIds(self
):
99 return [extension
['id'] for extension
in self
.GetExtensionsInfo()]
101 def _ReturnCrashingExtensions(self
, extensions
, group_size
, top_urls
):
102 """Returns the group of extensions that crashes (if any).
104 Install the given extensions in groups of group_size and return the
105 group of extensions that crashes (if any).
108 extensions: A list of extensions to install.
109 group_size: The number of extensions to install at one time.
110 top_urls: The list of top urls to visit.
113 The extensions in the crashing group or None if there is no crash.
116 num_extensions
= len(extensions
)
117 self
.RestartBrowser()
118 orig_extension_ids
= self
._GetInstalledExtensionIds
()
120 while curr_extension
< num_extensions
:
121 logging
.debug('New group of %d extensions.', group_size
)
122 group_end
= curr_extension
+ group_size
123 for extension
in extensions
[curr_extension
:group_end
]:
124 logging
.debug('Installing extension: %s', extension
)
125 self
.InstallExtension(extension
)
128 self
.NavigateToURL(url
)
130 def _LogAndReturnCrashing():
131 crashing_extensions
= extensions
[curr_extension
:group_end
]
132 logging
.debug('Crashing extensions: %s', crashing_extensions
)
133 return crashing_extensions
135 # If the browser has crashed, return the extensions in the failing group.
137 num_browser_windows
= self
.GetBrowserWindowCount()
139 return _LogAndReturnCrashing()
141 if not num_browser_windows
:
142 return _LogAndReturnCrashing()
144 # Uninstall all extensions that aren't installed by default.
145 new_extension_ids
= [id for id in self
._GetInstalledExtensionIds
()
146 if id not in orig_extension_ids
]
147 for extension_id
in new_extension_ids
:
148 self
.UninstallExtensionById(extension_id
)
150 curr_extension
= group_end
152 # None of the extensions crashed.
155 def _GetExtensionInfoById(self
, extensions
, id):
161 def ExtensionCrashes(self
):
162 """Add top extensions; confirm browser stays up when visiting top urls."""
163 # TODO: provide a way in pyauto to pass args to a test - take these as args
164 extensions_dir
= os
.path
.join(self
.DataDir(), 'extensions-tool')
165 urls_file
= os
.path
.join(self
.DataDir(), 'urls.txt')
167 error_msg
= 'The dir "%s" must exist' % os
.path
.abspath(extensions_dir
)
168 assert os
.path
.exists(extensions_dir
), error_msg
169 error_msg
= 'The file "%s" must exist' % os
.path
.abspath(urls_file
)
170 assert os
.path
.exists(urls_file
), error_msg
172 num_urls_to_visit
= 100
173 extensions_group_size
= 20
175 top_urls
= [l
.rstrip() for l
in
176 open(urls_file
).readlines()[:num_urls_to_visit
]]
178 failed_extensions
= glob
.glob(os
.path
.join(extensions_dir
, '*.crx'))
179 group_size
= extensions_group_size
181 while (group_size
and failed_extensions
):
182 failed_extensions
= self
._ReturnCrashingExtensions
(
183 failed_extensions
, group_size
, top_urls
)
184 group_size
= group_size
// 2
186 self
.assertFalse(failed_extensions
,
187 'Extension(s) in failing group: %s' % failed_extensions
)
189 def _InstallExtensionCheckDefaults(self
, crx_file
):
190 """Installs extension at extensions/|crx_file| and checks default status.
192 Checks that the installed extension is enabled and not allowed in incognito.
195 crx_file: Relative path from self.DataDir()/extensions to .crx extension
201 crx_file_path
= os
.path
.abspath(
202 os
.path
.join(self
.DataDir(), 'extensions', crx_file
))
203 ext_id
= self
.InstallExtension(crx_file_path
)
204 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
205 self
.assertTrue(extension
['is_enabled'],
206 msg
='Extension was not enabled on installation')
207 self
.assertFalse(extension
['allowed_in_incognito'],
208 msg
='Extension was allowed in incognito on installation.')
212 def _ExtensionValue(self
, ext_id
, key
):
213 """Returns the value of |key| for |ext_id|.
216 ext_id: The extension ID.
217 key: The key for which the extensions info value is required.
220 The value of extensions info |key| for |ext_id|.
222 return self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)[key
]
224 def _FileAccess(self
, ext_id
):
225 """Returns the value of newAllowFileAccess for |ext_id|.
228 ext_id: The extension ID.
231 The value of extensions settings newAllowFileAccess for |ext_id|.
233 extension_settings
= self
.GetPrefsInfo().Prefs()['extensions']['settings']
234 return extension_settings
[ext_id
]['newAllowFileAccess']
236 def testGetExtensionPermissions(self
):
237 """Ensures we can retrieve the host/api permissions for an extension.
239 This test assumes that the 'Bookmark Manager' extension exists in a fresh
242 extensions_info
= self
.GetExtensionsInfo()
243 bm_exts
= [x
for x
in extensions_info
if x
['name'] == 'Bookmark Manager']
244 self
.assertTrue(bm_exts
,
245 msg
='Could not find info for the Bookmark Manager '
249 permissions_host
= ext
['host_permissions']
250 self
.assertTrue(len(permissions_host
) == 2 and
251 'chrome://favicon/*' in permissions_host
and
252 'chrome://resources/*' in permissions_host
,
253 msg
='Unexpected host permissions information.')
255 permissions_api
= ext
['api_permissions']
256 print permissions_api
257 self
.assertTrue(len(permissions_api
) == 4 and
258 'bookmarks' in permissions_api
and
259 'bookmarkManagerPrivate' in permissions_api
and
260 'systemPrivate' in permissions_api
and
261 'tabs' in permissions_api
,
262 msg
='Unexpected API permissions information.')
264 def testSetExtensionStates(self
):
265 """Test setting different extension states."""
266 ext_id
= self
._InstallExtensionCheckDefaults
('google_talk.crx')
268 # Disable the extension and verify.
269 self
.SetExtensionStateById(ext_id
, enable
=False, allow_in_incognito
=False)
270 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
271 self
.assertFalse(extension
['is_enabled'])
273 # Enable extension and verify.
274 self
.SetExtensionStateById(ext_id
, enable
=True, allow_in_incognito
=False)
275 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
276 self
.assertTrue(extension
['is_enabled'])
278 # Allow extension in incognito mode and verify.
279 self
.SetExtensionStateById(ext_id
, enable
=True, allow_in_incognito
=True)
280 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
281 self
.assertTrue(extension
['allowed_in_incognito'])
283 # Disallow extension in incognito mode and verify.
284 self
.SetExtensionStateById(ext_id
, enable
=True, allow_in_incognito
=False)
285 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
286 self
.assertFalse(extension
['allowed_in_incognito'])
288 def testTriggerBrowserAction(self
):
289 """Test triggering browser action."""
290 dir_path
= os
.path
.abspath(
291 os
.path
.join(self
.DataDir(), 'extensions', 'trigger_actions',
293 ext_id
= self
.InstallExtension(dir_path
)
295 self
.NavigateToURL(self
.GetFileURLForDataPath('simple.html'))
297 self
.TriggerBrowserActionById(ext_id
)
299 # Verify that the browser action turned the background red.
300 self
.assertTrue(self
.WaitUntil(
301 lambda: self
.GetDOMValue('document.body.style.backgroundColor'),
302 expect_retval
='red'),
303 msg
='Browser action was not triggered.')
305 def testTriggerBrowserActionWithPopup(self
):
306 """Test triggering browser action that shows a popup."""
307 # Fails on Vista Chromium bot only. crbug.com/106620
308 if (self
.IsWinVista() and
309 self
.GetBrowserInfo()['properties']['branding'] == 'Chromium'):
311 dir_path
= os
.path
.abspath(
312 os
.path
.join(self
.DataDir(), 'extensions', 'trigger_actions',
313 'browser_action_popup'))
314 ext_id
= self
.InstallExtension(dir_path
)
316 self
.TriggerBrowserActionById(ext_id
)
318 # Verify that the extension popup is displayed.
319 popup
= self
.WaitUntilExtensionViewLoaded(
320 view_type
='EXTENSION_POPUP')
321 self
.assertTrue(popup
,
322 msg
='Browser action failed to display the popup (views=%s).' %
323 self
.GetBrowserInfo()['extension_views'])
325 def testTriggerPageAction(self
):
326 """Test triggering page action."""
327 dir_path
= os
.path
.abspath(
328 os
.path
.join(self
.DataDir(), 'extensions', 'trigger_actions',
330 ext_id
= self
.InstallExtension(dir_path
)
332 # Page action icon is displayed when a tab is created.
333 self
.NavigateToURL(self
.GetFileURLForDataPath('simple.html'))
334 self
.AppendTab(pyauto
.GURL('chrome://newtab'))
336 self
.assertTrue(self
.WaitUntil(
338 self
.GetBrowserInfo()['windows'][0]['visible_page_actions']),
339 msg
='Page action icon is not visible.')
341 self
.TriggerPageActionById(ext_id
)
343 # Verify that page action turned the background red.
344 self
.assertTrue(self
.WaitUntil(
345 lambda: self
.GetDOMValue('document.body.style.backgroundColor'),
346 expect_retval
='red'),
347 msg
='Page action was not triggered.')
349 def testTriggerPageActionWithPopup(self
):
350 """Test triggering page action that shows a popup."""
351 # Fails on Vista Chromium bot only. crbug.com/106620
352 if (self
.IsWinVista() and
353 self
.GetBrowserInfo()['properties']['branding'] == 'Chromium'):
355 dir_path
= os
.path
.abspath(
356 os
.path
.join(self
.DataDir(), 'extensions', 'trigger_actions',
357 'page_action_popup'))
358 ext_id
= self
.InstallExtension(dir_path
)
360 # Page action icon is displayed when a tab is created.
361 self
.AppendTab(pyauto
.GURL('chrome://newtab'))
363 self
.assertTrue(self
.WaitUntil(
365 self
.GetBrowserInfo()['windows'][0]['visible_page_actions']),
366 msg
='Page action icon is not visible.')
368 self
.TriggerPageActionById(ext_id
)
370 # Verify that the extension popup is displayed.
371 popup
= self
.WaitUntilExtensionViewLoaded(
372 view_type
='EXTENSION_POPUP')
373 self
.assertTrue(popup
,
374 msg
='Page action failed to display the popup (views=%s).' %
375 self
.GetBrowserInfo()['extension_views'])
377 def testAdblockExtensionCrash(self
):
378 """Test AdBlock extension does not cause a browser crash."""
379 ext_id
= self
._InstallExtensionCheckDefaults
('adblock.crx')
381 self
.RestartBrowser(clear_profile
=False)
382 extension
= self
._GetExtensionInfoById
(self
.GetExtensionsInfo(), ext_id
)
383 self
.assertTrue(extension
['is_enabled'])
384 self
.assertFalse(extension
['allowed_in_incognito'])
386 def testDisableEnableExtension(self
):
387 """Tests that an extension can be disabled and enabled with the UI."""
388 ext_id
= self
._InstallExtensionCheckDefaults
('good.crx')
391 driver
= self
.NewWebDriver()
392 ext_page
= ExtensionsPage(driver
)
393 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
394 ext_page
.SetEnabled(ext_id
, False)
395 self
.WaitUntil(self
._ExtensionValue
, args
=[ext_id
, 'is_enabled'],
397 self
.assertFalse(self
._ExtensionValue
(ext_id
, 'is_enabled'),
398 msg
='Extension did not get disabled.')
401 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
402 ext_page
.SetEnabled(ext_id
, True)
403 self
.WaitUntil(self
._ExtensionValue
, args
=[ext_id
, 'is_enabled'],
405 self
.assertTrue(self
._ExtensionValue
(ext_id
, 'is_enabled'),
406 msg
='Extension did not get enabled.')
408 def testAllowIncognitoExtension(self
):
409 """Tests allowing and disallowing an extension in incognito mode."""
410 ext_id
= self
._InstallExtensionCheckDefaults
('good.crx')
412 # Allow in incognito.
413 driver
= self
.NewWebDriver()
414 ext_page
= ExtensionsPage(driver
)
415 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
416 ext_page
.SetAllowInIncognito(ext_id
, True)
418 # Check extension now allowed in incognito.
419 self
.WaitUntil(self
._ExtensionValue
, args
=[ext_id
, 'allowed_in_incognito'],
421 self
.assertTrue(self
._ExtensionValue
(ext_id
, 'allowed_in_incognito'),
422 msg
='Extension did not get allowed in incognito.')
424 # Disallow in incognito.
425 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
426 ext_page
.SetAllowInIncognito(ext_id
, False)
428 # Check extension now disallowed in incognito.
429 self
.WaitUntil(self
._ExtensionValue
, args
=[ext_id
, 'allowed_in_incognito'],
431 self
.assertFalse(self
._ExtensionValue
(ext_id
, 'allowed_in_incognito'),
432 msg
='Extension did not get disallowed in incognito.')
434 def testAllowAccessFileURLs(self
):
435 """Tests disallowing and allowing and extension access to file URLs."""
436 ext_id
= self
._InstallExtensionCheckDefaults
(os
.path
.join('permissions',
439 # Check extension allowed access to file URLs by default.
440 extension_settings
= self
.GetPrefsInfo().Prefs()['extensions']['settings']
441 self
.assertTrue(extension_settings
[ext_id
]['newAllowFileAccess'],
442 msg
='Extension was not allowed access to file URLs on '
445 # Disallow access to file URLs.
446 driver
= self
.NewWebDriver()
447 ext_page
= ExtensionsPage(driver
)
448 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
449 ext_page
.SetAllowAccessFileURLs(ext_id
, False)
451 # Check that extension does not have access to file URLs.
452 self
.WaitUntil(self
._FileAccess
, args
=[ext_id
], expect_retval
=False)
453 self
.assertFalse(self
._FileAccess
(ext_id
),
454 msg
='Extension did not have access to file URLs denied.')
456 # Allow access to file URLs.
457 self
.WaitUntil(ext_page
.CheckExtensionVisible
, args
=[ext_id
])
458 ext_page
.SetAllowAccessFileURLs(ext_id
, True)
460 # Check that extension now has access to file URLs.
461 self
.WaitUntil(self
._FileAccess
, args
=[ext_id
], expect_retval
=True)
462 self
.assertTrue(self
._FileAccess
(ext_id
),
463 msg
='Extension did not have access to file URLs granted.')
466 if __name__
== '__main__':
467 pyauto_functional
.Main()