You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
10 KiB
287 lines
10 KiB
# Lint as: python2, python3
|
|
# Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""
|
|
This module allows tests to interact with the Chrome Web Store (CWS)
|
|
using ChromeDriver. They should inherit from the webstore_test class,
|
|
and should override the run() method.
|
|
"""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import logging
|
|
import six
|
|
from six.moves import range
|
|
from six.moves import zip
|
|
import time
|
|
|
|
from autotest_lib.client.bin import test
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib.cros import chromedriver
|
|
from autotest_lib.client.common_lib.global_config import global_config
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.support import expected_conditions
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
|
|
# How long to wait, in seconds, for an app to launch. This is larger
|
|
# than it needs to be, because it might be slow on older Chromebooks
|
|
_LAUNCH_DELAY = 4
|
|
|
|
# How long to wait before entering the password when logging in to the CWS
|
|
_ENTER_PASSWORD_DELAY = 2
|
|
|
|
# How long to wait before entering payment info
|
|
_PAYMENT_DELAY = 5
|
|
|
|
def enum(*enumNames):
|
|
"""
|
|
Creates an enum. Returns an enum object with a value for each enum
|
|
name, as well as from_string and to_string mappings.
|
|
|
|
@param enumNames: The strings representing the values of the enum
|
|
"""
|
|
enums = dict(zip(enumNames, list(range(len(enumNames)))))
|
|
reverse = dict((value, key) for key, value in six.iteritems(enums))
|
|
enums['from_string'] = enums
|
|
enums['to_string'] = reverse
|
|
return type('Enum', (), enums)
|
|
|
|
# TODO: staging and PNL don't work in these tests (crbug/396660)
|
|
TestEnv = enum('staging', 'pnl', 'prod', 'sandbox')
|
|
|
|
ItemType = enum(
|
|
'hosted_app',
|
|
'packaged_app',
|
|
'chrome_app',
|
|
'extension',
|
|
'theme',
|
|
)
|
|
|
|
# NOTE: paid installs don't work right now
|
|
InstallType = enum(
|
|
'free',
|
|
'free_trial',
|
|
'paid',
|
|
)
|
|
|
|
def _labeled_button(label):
|
|
"""
|
|
Returns a button with the class webstore-test-button-label and the
|
|
specified label
|
|
|
|
@param label: The label on the button
|
|
"""
|
|
return ('//div[contains(@class,"webstore-test-button-label") '
|
|
'and text()="' + label + '"]')
|
|
|
|
def _install_type_click_xpath(item_type, install_type):
|
|
"""
|
|
Returns the XPath of the button to install an item of the given type.
|
|
|
|
@param item_type: The type of the item to install
|
|
@param install_type: The type of installation being used
|
|
"""
|
|
if install_type == InstallType.free:
|
|
return _labeled_button('Free')
|
|
elif install_type == InstallType.free_trial:
|
|
# Both of these cases return buttons that say "Add to Chrome",
|
|
# but they are actually different buttons with only one being
|
|
# visible at a time.
|
|
if item_type == ItemType.hosted_app:
|
|
return ('//div[@id="cxdialog-install-paid-btn" and '
|
|
'@aria-label="Add to Chrome"]')
|
|
else:
|
|
return _labeled_button('Add to Chrome')
|
|
else:
|
|
return ('//div[contains(@aria-label,"Buy for") '
|
|
'and not(contains(@style,"display: none"))]')
|
|
|
|
def _get_chrome_flags(test_env):
|
|
"""
|
|
Returns the Chrome flags for the given test environment.
|
|
"""
|
|
flags = ['--apps-gallery-install-auto-confirm-for-tests=accept']
|
|
if test_env == TestEnv.prod:
|
|
return flags
|
|
|
|
url_middle = {
|
|
TestEnv.staging: 'staging.corp',
|
|
TestEnv.sandbox: 'staging.sandbox',
|
|
TestEnv.pnl: 'prod-not-live.corp'
|
|
}[test_env]
|
|
download_url_middle = {
|
|
TestEnv.staging: 'download-staging.corp',
|
|
TestEnv.sandbox: 'download-staging.sandbox',
|
|
TestEnv.pnl: 'omaha.sandbox'
|
|
}[test_env]
|
|
flags.append('--apps-gallery-url=https://webstore-' + url_middle +
|
|
'.google.com')
|
|
flags.append('--apps-gallery-update-url=https://' + download_url_middle +
|
|
'.google.com/service/update2/crx')
|
|
logging.info('Using flags %s', flags)
|
|
return flags
|
|
|
|
|
|
class webstore_test(test.test):
|
|
"""
|
|
The base class for tests that interact with the web store.
|
|
|
|
Subclasses must define run(), but should not override run_once().
|
|
Subclasses should use methods in this module such as install_item,
|
|
but they can also use the driver directly if they need to.
|
|
"""
|
|
|
|
def initialize(self, test_env=TestEnv.sandbox,
|
|
account='cwsbotdeveloper1@gmail.com'):
|
|
"""
|
|
Initialize the test.
|
|
|
|
@param test_env: The test environment to use
|
|
"""
|
|
super(webstore_test, self).initialize()
|
|
|
|
self.username = account
|
|
self.password = global_config.get_config_value(
|
|
'CLIENT', 'webstore_test_password', type=str)
|
|
|
|
self.test_env = test_env
|
|
self._chrome_flags = _get_chrome_flags(test_env)
|
|
self.webstore_url = {
|
|
TestEnv.staging:
|
|
'https://webstore-staging.corp.google.com',
|
|
TestEnv.sandbox:
|
|
'https://webstore-staging.sandbox.google.com/webstore',
|
|
TestEnv.pnl:
|
|
'https://webstore-prod-not-live.corp.google.com/webstore',
|
|
TestEnv.prod:
|
|
'https://chrome.google.com/webstore'
|
|
}[test_env]
|
|
|
|
|
|
def build_url(self, page):
|
|
"""
|
|
Builds a webstore URL for the specified page.
|
|
|
|
@param page: the page to build a URL for
|
|
"""
|
|
return self.webstore_url + page + "?gl=US"
|
|
|
|
|
|
def detail_page(self, item_id):
|
|
"""
|
|
Returns the URL of the detail page for the given item
|
|
|
|
@param item_id: The item ID
|
|
"""
|
|
return self.build_url("/detail/" + item_id)
|
|
|
|
|
|
def wait_for(self, xpath):
|
|
"""
|
|
Waits until the element specified by the given XPath is visible
|
|
|
|
@param xpath: The xpath of the element to wait for
|
|
"""
|
|
self._wait.until(expected_conditions.visibility_of_element_located(
|
|
(By.XPATH, xpath)))
|
|
|
|
|
|
def run_once(self, **kwargs):
|
|
with chromedriver.chromedriver(
|
|
username=self.username,
|
|
password=self.password,
|
|
extra_chrome_flags=self._chrome_flags) \
|
|
as chromedriver_instance:
|
|
self.driver = chromedriver_instance.driver
|
|
self.driver.implicitly_wait(15)
|
|
self._wait = WebDriverWait(self.driver, 20)
|
|
logging.info('Running test on test environment %s',
|
|
TestEnv.to_string[self.test_env])
|
|
self.run(**kwargs)
|
|
|
|
|
|
def run(self):
|
|
"""
|
|
Runs the test. Should be overridden by subclasses.
|
|
"""
|
|
raise error.TestError('The test needs to override run()')
|
|
|
|
|
|
def install_item(self, item_id, item_type, install_type):
|
|
"""
|
|
Installs an item from the CWS.
|
|
|
|
@param item_id: The ID of the item to install
|
|
(a 32-char string of letters)
|
|
@param item_type: The type of the item to install
|
|
@param install_type: The type of installation
|
|
(free, free trial, or paid)
|
|
"""
|
|
logging.info('Installing item %s of type %s with install_type %s',
|
|
item_id, ItemType.to_string[item_type],
|
|
InstallType.to_string[install_type])
|
|
|
|
# We need to go to the CWS home page before going to the detail
|
|
# page due to a bug in the CWS
|
|
self.driver.get(self.webstore_url)
|
|
self.driver.get(self.detail_page(item_id))
|
|
|
|
install_type_click_xpath = _install_type_click_xpath(
|
|
item_type, install_type)
|
|
if item_type == ItemType.extension or item_type == ItemType.theme:
|
|
post_install_xpath = (
|
|
'//div[@aria-label="Added to Chrome" '
|
|
' and not(contains(@style,"display: none"))]')
|
|
else:
|
|
post_install_xpath = _labeled_button('Launch app')
|
|
|
|
# In this case we need to sign in again
|
|
if install_type != InstallType.free:
|
|
button_xpath = _labeled_button('Sign in to add')
|
|
logging.info('Clicking button %s', button_xpath)
|
|
self.driver.find_element_by_xpath(button_xpath).click()
|
|
time.sleep(_ENTER_PASSWORD_DELAY)
|
|
password_field = self.driver.find_element_by_xpath(
|
|
'//input[@id="Passwd"]')
|
|
password_field.send_keys(self.password)
|
|
self.driver.find_element_by_xpath('//input[@id="signIn"]').click()
|
|
|
|
logging.info('Clicking %s', install_type_click_xpath)
|
|
self.driver.find_element_by_xpath(install_type_click_xpath).click()
|
|
|
|
if install_type == InstallType.paid:
|
|
handle = self.driver.current_window_handle
|
|
iframe = self.driver.find_element_by_xpath(
|
|
'//iframe[contains(@src, "sandbox.google.com/checkout")]')
|
|
self.driver.switch_to_frame(iframe)
|
|
self.driver.find_element_by_id('purchaseButton').click()
|
|
time.sleep(_PAYMENT_DELAY) # Wait for animation to finish
|
|
self.driver.find_element_by_id('finishButton').click()
|
|
self.driver.switch_to_window(handle)
|
|
|
|
self.wait_for(post_install_xpath)
|
|
|
|
|
|
def launch_app(self, app_id):
|
|
"""
|
|
Launches an app. Verifies that it launched by verifying that
|
|
a new tab/window was opened.
|
|
|
|
@param app_id: The ID of the app to run
|
|
"""
|
|
logging.info('Launching app %s', app_id)
|
|
num_handles_before = len(self.driver.window_handles)
|
|
self.driver.get(self.webstore_url)
|
|
self.driver.get(self.detail_page(app_id))
|
|
launch_button = self.driver.find_element_by_xpath(
|
|
_labeled_button('Launch app'))
|
|
launch_button.click();
|
|
time.sleep(_LAUNCH_DELAY) # Wait for the app to launch
|
|
num_handles_after = len(self.driver.window_handles)
|
|
if num_handles_after <= num_handles_before:
|
|
raise error.TestError('App failed to launch')
|