1ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# Copyright 2014 The Chromium OS Authors. All rights reserved.
2ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# Use of this source code is governed by a BSD-style license that can be
3ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# found in the LICENSE file.
4ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
5ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard"""
6ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan StoddardThis module allows tests to interact with the Chrome Web Store (CWS)
7ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardusing ChromeDriver. They should inherit from the webstore_test class,
8ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardand should override the run() method.
9ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard"""
10ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
11ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardimport logging
12ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardimport time
13ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
14ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom autotest_lib.client.bin import test
15ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom autotest_lib.client.common_lib import error
16ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom autotest_lib.client.common_lib.cros import chromedriver
17ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom autotest_lib.client.common_lib.global_config import global_config
18ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom selenium.webdriver.common.by import By
19ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom selenium.webdriver.support import expected_conditions
20ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardfrom selenium.webdriver.support.ui import WebDriverWait
21ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
22ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# How long to wait, in seconds, for an app to launch. This is larger
23ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# than it needs to be, because it might be slow on older Chromebooks
24ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard_LAUNCH_DELAY = 4
25ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
26ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# How long to wait before entering the password when logging in to the CWS
27ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard_ENTER_PASSWORD_DELAY = 2
28ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
29ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# How long to wait before entering payment info
30ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard_PAYMENT_DELAY = 5
31ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
32ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddarddef enum(*enumNames):
33ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
34ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Creates an enum. Returns an enum object with a value for each enum
35ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    name, as well as from_string and to_string mappings.
36ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
37ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    @param enumNames: The strings representing the values of the enum
38ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
39ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    enums = dict(zip(enumNames, range(len(enumNames))))
40ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    reverse = dict((value, key) for key, value in enums.iteritems())
41ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    enums['from_string'] = enums
42ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    enums['to_string'] = reverse
43ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    return type('Enum', (), enums)
44ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
45ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# TODO: staging and PNL don't work in these tests (crbug/396660)
46ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan StoddardTestEnv = enum('staging', 'pnl', 'prod', 'sandbox')
47ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
48ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan StoddardItemType = enum(
49ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'hosted_app',
50ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'packaged_app',
51ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'chrome_app',
52ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'extension',
53ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'theme',
54ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard)
55ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
56ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard# NOTE: paid installs don't work right now
57ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan StoddardInstallType = enum(
58ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'free',
59ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'free_trial',
60ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    'paid',
61ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard)
62ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
63ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddarddef _labeled_button(label):
64ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
65ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Returns a button with the class webstore-test-button-label and the
66ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    specified label
67ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
68ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    @param label: The label on the button
69ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
70ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    return ('//div[contains(@class,"webstore-test-button-label") '
71ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            'and text()="' + label + '"]')
72ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
73ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddarddef _install_type_click_xpath(item_type, install_type):
74ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
75ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Returns the XPath of the button to install an item of the given type.
76ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
77ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    @param item_type: The type of the item to install
78ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    @param install_type: The type of installation being used
79ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
80ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    if install_type == InstallType.free:
81ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        return _labeled_button('Free')
82ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    elif install_type == InstallType.free_trial:
83ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # Both of these cases return buttons that say "Add to Chrome",
84ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # but they are actually different buttons with only one being
85ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # visible at a time.
86ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        if item_type == ItemType.hosted_app:
87ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            return ('//div[@id="cxdialog-install-paid-btn" and '
88ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    '@aria-label="Add to Chrome"]')
89ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        else:
90ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            return _labeled_button('Add to Chrome')
91ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    else:
92ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        return ('//div[contains(@aria-label,"Buy for") '
93ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                'and not(contains(@style,"display: none"))]')
94ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
95ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddarddef _get_chrome_flags(test_env):
96ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
97ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Returns the Chrome flags for the given test environment.
98ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
99ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    flags = ['--apps-gallery-install-auto-confirm-for-tests=accept']
100ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    if test_env == TestEnv.prod:
101ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        return flags
102ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
103ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    url_middle = {
104ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.staging: 'staging.corp',
105ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.sandbox: 'staging.sandbox',
106ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.pnl: 'prod-not-live.corp'
107ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            }[test_env]
108ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    download_url_middle = {
109ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.staging: 'download-staging.corp',
110ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.sandbox: 'download-staging.sandbox',
111ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            TestEnv.pnl: 'omaha.sandbox'
112ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            }[test_env]
113ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    flags.append('--apps-gallery-url=https://webstore-' + url_middle +
114ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            '.google.com')
115ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    flags.append('--apps-gallery-update-url=https://' + download_url_middle +
116ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            '.google.com/service/update2/crx')
117ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    logging.info('Using flags %s', flags)
118ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    return flags
119ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
120ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
121ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddardclass webstore_test(test.test):
122ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
123ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    The base class for tests that interact with the web store.
124ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
125ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Subclasses must define run(), but should not override run_once().
126ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    Subclasses should use methods in this module such as install_item,
127ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    but they can also use the driver directly if they need to.
128ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    """
129ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
130ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def initialize(self, test_env=TestEnv.sandbox,
131ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                   account='cwsbotdeveloper1@gmail.com'):
132ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
133ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Initialize the test.
134ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
135ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param test_env: The test environment to use
136ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
137ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        super(webstore_test, self).initialize()
138ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
139ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.username = account
140ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.password = global_config.get_config_value(
141ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                'CLIENT', 'webstore_test_password', type=str)
142ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
143ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.test_env = test_env
144ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self._chrome_flags = _get_chrome_flags(test_env)
145ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.webstore_url = {
146ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                TestEnv.staging:
147ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    'https://webstore-staging.corp.google.com',
148ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                TestEnv.sandbox:
149ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    'https://webstore-staging.sandbox.google.com/webstore',
150ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                TestEnv.pnl:
151ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    'https://webstore-prod-not-live.corp.google.com/webstore',
152ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                TestEnv.prod:
153ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    'https://chrome.google.com/webstore'
154ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                }[test_env]
155ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
156ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
157ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def build_url(self, page):
158ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
159ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Builds a webstore URL for the specified page.
160ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
161ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param page: the page to build a URL for
162ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
163ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        return self.webstore_url + page + "?gl=US"
164ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
165ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
166ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def detail_page(self, item_id):
167ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
168ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Returns the URL of the detail page for the given item
169ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
170ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param item_id: The item ID
171ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
172ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        return self.build_url("/detail/" + item_id)
173ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
174ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
175ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def wait_for(self, xpath):
176ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
177ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Waits until the element specified by the given XPath is visible
178ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
179ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param xpath: The xpath of the element to wait for
180ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
181ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self._wait.until(expected_conditions.visibility_of_element_located(
182ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                (By.XPATH, xpath)))
183ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
184ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
185ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def run_once(self, **kwargs):
186ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        with chromedriver.chromedriver(
187ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                username=self.username,
188ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                password=self.password,
189ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                extra_chrome_flags=self._chrome_flags) \
190ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                as chromedriver_instance:
191ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver = chromedriver_instance.driver
192ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.implicitly_wait(15)
193ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self._wait = WebDriverWait(self.driver, 20)
194ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            logging.info('Running test on test environment %s',
195ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    TestEnv.to_string[self.test_env])
196ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.run(**kwargs)
197ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
198ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
199ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def run(self):
200ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
201ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Runs the test. Should be overridden by subclasses.
202ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
203ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        raise error.TestError('The test needs to override run()')
204ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
205ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
206ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def install_item(self, item_id, item_type, install_type):
207ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
208ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Installs an item from the CWS.
209ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
210ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param item_id: The ID of the item to install
211ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                (a 32-char string of letters)
212ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param item_type: The type of the item to install
213ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param install_type: The type of installation
214ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                (free, free trial, or paid)
215ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
216ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        logging.info('Installing item %s of type %s with install_type %s',
217ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                item_id, ItemType.to_string[item_type],
218ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                InstallType.to_string[install_type])
219ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
220ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # We need to go to the CWS home page before going to the detail
221ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # page due to a bug in the CWS
222ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.driver.get(self.webstore_url)
223ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.driver.get(self.detail_page(item_id))
224ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
225ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        install_type_click_xpath = _install_type_click_xpath(
226ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                item_type, install_type)
227ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        if item_type == ItemType.extension or item_type == ItemType.theme:
228ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            post_install_xpath = (
229ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                '//div[@aria-label="Added to Chrome" '
230ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                ' and not(contains(@style,"display: none"))]')
231ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        else:
232ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            post_install_xpath = _labeled_button('Launch app')
233ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
234ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        # In this case we need to sign in again
235ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        if install_type != InstallType.free:
236ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            button_xpath = _labeled_button('Sign in to add')
237ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            logging.info('Clicking button %s', button_xpath)
238ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.find_element_by_xpath(button_xpath).click()
239ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            time.sleep(_ENTER_PASSWORD_DELAY)
240ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            password_field = self.driver.find_element_by_xpath(
241ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                    '//input[@id="Passwd"]')
242ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            password_field.send_keys(self.password)
243ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.find_element_by_xpath('//input[@id="signIn"]').click()
244ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
245ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        logging.info('Clicking %s', install_type_click_xpath)
246ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.driver.find_element_by_xpath(install_type_click_xpath).click()
247ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
248ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        if install_type == InstallType.paid:
249ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            handle = self.driver.current_window_handle
250ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            iframe = self.driver.find_element_by_xpath(
251ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard                '//iframe[contains(@src, "sandbox.google.com/checkout")]')
252ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.switch_to_frame(iframe)
253ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.find_element_by_id('purchaseButton').click()
254ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            time.sleep(_PAYMENT_DELAY) # Wait for animation to finish
255ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.find_element_by_id('finishButton').click()
256ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            self.driver.switch_to_window(handle)
257ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
258ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.wait_for(post_install_xpath)
259ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
260ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
261ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard    def launch_app(self, app_id):
262ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
263ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        Launches an app. Verifies that it launched by verifying that
264ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        a new tab/window was opened.
265ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard
266ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        @param app_id: The ID of the app to run
267ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        """
268ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        logging.info('Launching app %s', app_id)
269ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        num_handles_before = len(self.driver.window_handles)
270ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.driver.get(self.webstore_url)
271ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        self.driver.get(self.detail_page(app_id))
272ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        launch_button = self.driver.find_element_by_xpath(
273ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            _labeled_button('Launch app'))
274ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        launch_button.click();
275ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        time.sleep(_LAUNCH_DELAY) # Wait for the app to launch
276ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        num_handles_after = len(self.driver.window_handles)
277ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard        if num_handles_after <= num_handles_before:
278ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard            raise error.TestError('App failed to launch')
279