Merge lp:~canonical-platform-qa/webapps-core/x-test into lp:webapps-core

Proposed by Leo Arias
Status: Merged
Approved by: Alexandre Abreu
Approved revision: 120
Merged at revision: 113
Proposed branch: lp:~canonical-platform-qa/webapps-core/x-test
Merge into: lp:webapps-core
Diff against target: 446 lines (+409/-9)
4 files modified
README (+104/-7)
webapp-amazon/manifest.json (+15/-2)
webapp-amazon/tests/autopilot/amazon_webapp/__init__.py (+138/-0)
webapp-amazon/tests/autopilot/amazon_webapp/test_amazon_webapp.py (+152/-0)
To merge this branch: bzr merge lp:~canonical-platform-qa/webapps-core/x-test
Reviewer Review Type Date Requested Status
Alexandre Abreu (community) Approve
Canonical Platform QA Team Pending
Review via email: mp+249580@code.launchpad.net

Commit message

Added an initial selenium + autopilot test for a webapp. It runs locally, on qemu and on the phone.

To post a comment you must log in.
Revision history for this message
Federico Gimenez (fgimenez) wrote :

Some inline comments, thanks!

Revision history for this message
David Barth (dbarth) wrote :
Download full text (18.3 KiB)

Would you have a rebuild handy for Trusty? Like app developers should, I
try to stick to Ubuntu LTS for development tasks.

On Mon, Feb 16, 2015 at 10:36 AM, Federico Gimenez <email address hidden>
wrote:

> Some inline comments, thanks!
>
>
> Diff comments:
>
> > === modified file 'README'
> > --- README 2013-09-13 08:20:52 +0000
> > +++ README 2015-02-14 05:29:25 +0000
> > @@ -1,9 +1,106 @@
> > -This package provides a set of default web application versions for
> popular web services.
> > +Core Webapps
> > +============
> > +
> > +This package provides a set of default web application versions for
> popular
> > +web services.
> >
> > The debian package itself serves 2 purposes:
> > -- generate click packages in a clean environment, that can be
> integrated with the rest of our toolchain
> > -- package a set of icons for the webapps, while click doesn't support
> icons directly (temporary)
> > -
> > -To generate the click packages directly, ie without the debian package:
> > -
> > -./build.sh
> > +- generate click packages in a clean environment, that can be
> integrated with
> > +the rest of our toolchain
> > +- package a set of icons for the webapps, while click doesn't support
> icons
> > +directly (temporary)
> > +
> > +Building the click packages
> > +===========================
> > +
> > +To generate the click packages directly, ie without the debian package::
> > +
> > + $ ./build.sh
> > +
> > +Running the user acceptance tests
> > +=================================
> > +
> > +Run tests locally
> > +-----------------
> > +
> > +The tests need an updated selenium package and the oxide webdriver::
> > +
> > + $ sudo add-apt-repository ppa:canonical-platform-qa/selenium
> > + $ sudo apt-get update
> > + $ sudo apt-get install python3-selenium oxideqt-chromedriver
> > +
> > +To run the user acceptance tests from the source branch::
> > +
> > + $ cd webapp-amazon/tests/autopilot
> > + $ autopilot3 run --config from_source amazon_webapp
> > +
> > +Run tests in the phone
> > +----------------------
> > +
> > +First flash the phone with developer mode enabled::
> > +
> > + $ ubuntu-device-flash --developer-mode --password 0000 \
> > + --channel="ubuntu-touch/devel-proposed" --wipe
> > +
> > +When the phone is flashed, complete the welcome wizard.
> > +
> > +To run the user accceptance tests into an Ubuntu phone with a read-only
> image::
> > +
> > + $ adt-run --click-source webapp-amazon/ \
> > + --click
> build/com.ubuntu.developer.webapps.webapp-amazon_*_all.click \
> > + -o /tmp/adt-webapps-tests \
> > + --setup-commands \
> > + 'mount -o remount,rw /' \
> > + --setup-commands \
> > + 'apt-add-repository -y ppa:canonical-platform-qa/selenium' \
> > + --setup-commands \
> > + 'apt-get --no-list-cleanup update -o
> Dir::Etc::SourceList=/dev/null' \
> > + --setup-commands \
> > + 'sync; sleep 2; mount -o remount,ro /' \
> > + --- ssh -s adb -- -p 0000
> > +
> > +Run tests in a virtual machine
> > +------------------------------
> > +
> > +The user acceptance tests also can be run against a built and installed
> click
> > +package, under qemu. To do so,...

Revision history for this message
Leo Arias (elopio) wrote :

David, I'm not sure what build are you asking for. Doesn't ./build.sh work on trusty?
I only have vivid here.

120. By Leo Arias

Extracted a method to get dependencies path.

Revision history for this message
Leo Arias (elopio) wrote :

Replied and pushed to federico's comments.

Revision history for this message
Federico Gimenez (fgimenez) wrote :

Thanks Leo, some inline comments, cheers!

Revision history for this message
Leo Arias (elopio) :
Revision history for this message
Federico Gimenez (fgimenez) wrote :

One inline comment, all working now :)

Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

Could we make the webapp agnostic bits (chromedriver related, PageObject, etc.) in an emulator?

Revision history for this message
Leo Arias (elopio) wrote :

Do you mean, to move the things that will be common to all the webapps to a module that all of them can share?

Yes we can, but we will need to package those common bits into a deb. It can be called something like webapp-test-helpers, and every test in here will import it.
I imagine that shared package will have a base test class, a base page object, and all the helpers to get chromedriver running.

It will get a little weird if they are in the same branch though, because adt-run will use the archive to install dependencies in the test bed, so if a test needs an update in the common helpers, the common helpers will have to be pushed to the archive first before being able to run the new tests.

Does that answer your question? Or did I miss the point?

Revision history for this message
Alexandre Abreu (abreu-alexandre) :
review: Approve
Revision history for this message
Leo Arias (elopio) wrote :

woohoo \o/
Let us know if you need some help from us extracting the pieces that will be common to all the apps. Well, actually, let Julien know :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2013-09-13 08:20:52 +0000
3+++ README 2015-02-17 19:29:56 +0000
4@@ -1,9 +1,106 @@
5-This package provides a set of default web application versions for popular web services.
6+Core Webapps
7+============
8+
9+This package provides a set of default web application versions for popular
10+web services.
11
12 The debian package itself serves 2 purposes:
13-- generate click packages in a clean environment, that can be integrated with the rest of our toolchain
14-- package a set of icons for the webapps, while click doesn't support icons directly (temporary)
15-
16-To generate the click packages directly, ie without the debian package:
17-
18-./build.sh
19+- generate click packages in a clean environment, that can be integrated with
20+the rest of our toolchain
21+- package a set of icons for the webapps, while click doesn't support icons
22+directly (temporary)
23+
24+Building the click packages
25+===========================
26+
27+To generate the click packages directly, ie without the debian package::
28+
29+ $ ./build.sh
30+
31+Running the user acceptance tests
32+=================================
33+
34+Run tests locally
35+-----------------
36+
37+The tests need an updated selenium package and the oxide webdriver::
38+
39+ $ sudo add-apt-repository ppa:canonical-platform-qa/selenium
40+ $ sudo apt-get update
41+ $ sudo apt-get install python3-selenium oxideqt-chromedriver
42+
43+To run the user acceptance tests from the source branch::
44+
45+ $ cd webapp-amazon/tests/autopilot
46+ $ autopilot3 run --config from_source amazon_webapp
47+
48+Run tests in the phone
49+----------------------
50+
51+First flash the phone with developer mode enabled::
52+
53+ $ ubuntu-device-flash --developer-mode --password 0000 \
54+ --channel="ubuntu-touch/devel-proposed" --wipe
55+
56+When the phone is flashed, complete the welcome wizard.
57+
58+To run the user accceptance tests into an Ubuntu phone with a read-only image::
59+
60+ $ adt-run --click-source webapp-amazon/ \
61+ --click build/com.ubuntu.developer.webapps.webapp-amazon_*_all.click \
62+ -o /tmp/adt-webapps-tests \
63+ --setup-commands \
64+ 'mount -o remount,rw /' \
65+ --setup-commands \
66+ 'apt-add-repository -y ppa:canonical-platform-qa/selenium' \
67+ --setup-commands \
68+ 'apt-get --no-list-cleanup update -o Dir::Etc::SourceList=/dev/null' \
69+ --setup-commands \
70+ 'sync; sleep 2; mount -o remount,ro /' \
71+ --- ssh -s adb -- -p 0000
72+
73+Run tests in a virtual machine
74+------------------------------
75+
76+The user acceptance tests also can be run against a built and installed click
77+package, under qemu. To do so, install autopkgtest and qemu::
78+
79+ $ sudo apt-get install autopkgtest qemu
80+
81+After installing autopkgtest and qemu, you need to build an image for qemu
82+to use. We use vivid here, as building an image to closely resemble the actual
83+stable phone images is quite difficult. When this is easier in the future, we
84+will switch to using stable phone images for this. We output the image to ~/
85+rather than the current directory, so it will be in a safer place to avoid
86+rebuilding images every time. You can store it in any directory you wish.
87+
88+::
89+
90+ $ adt-buildvm-ubuntu-cloud -r vivid -a amd64 -o ~/
91+
92+Then the tests may be run using adt-run with the qemu virtualization host.
93+The output directory option here can be wherever you like, and is where the
94+test artifacts will be placed. The ordering of the arguments to adt-run is
95+important so try to keep them in this order.
96+
97+::
98+
99+ $ adt-run --click-source webapp-amazon/ \
100+ --click build/com.ubuntu.developer.webapps.webapp-amazon_*_all.click \
101+ -o /tmp/adt-webapps-tests \
102+ --setup-commands ubuntu-touch-session \
103+ --setup-commands \
104+ 'add-apt-repository -y ppa:canonical-platform-qa/selenium' \
105+ -U --apt-pocket proposed \
106+ --- qemu ~/adt-vivid-amd64-cloud.img
107+
108+See the detailed results
109+------------------------
110+
111+To examine the test results, which are in subunit format, additional tools are
112+required::
113+
114+ $ sudo add-apt-repository ppa:thomir/trv
115+ $ sudo apt-get update
116+ $ sudo apt-get install trv
117+ $ trv /tmp/adt-webapps-tests/artifacts/autopilot.subunit
118
119=== modified file 'webapp-amazon/manifest.json'
120--- webapp-amazon/manifest.json 2014-09-19 16:29:06 +0000
121+++ webapp-amazon/manifest.json 2015-02-17 19:29:56 +0000
122@@ -11,6 +11,19 @@
123 "maintainer": "Webapps Team <webapps@lists.launchpad.net>",
124 "name": "com.ubuntu.developer.webapps.webapp-amazon",
125 "title": "webapp-amazon",
126- "version": "1.0.10"
127+ "version": "1.0.10",
128+ "x-test": {
129+ "autopilot": {
130+ "autopilot_module": "amazon_webapp",
131+ "depends": [
132+ "click",
133+ "dpkg-dev",
134+ "qtdeclarative5-ubuntu-ui-toolkit-plugin",
135+ "ubuntu-sdk-libs",
136+ "oxideqt-chromedriver",
137+ "python3-selenium",
138+ "webapp-container"
139+ ]
140+ }
141+ }
142 }
143-
144
145=== added directory 'webapp-amazon/tests'
146=== added directory 'webapp-amazon/tests/autopilot'
147=== added directory 'webapp-amazon/tests/autopilot/amazon_webapp'
148=== added file 'webapp-amazon/tests/autopilot/amazon_webapp/__init__.py'
149--- webapp-amazon/tests/autopilot/amazon_webapp/__init__.py 1970-01-01 00:00:00 +0000
150+++ webapp-amazon/tests/autopilot/amazon_webapp/__init__.py 2015-02-17 19:29:56 +0000
151@@ -0,0 +1,138 @@
152+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
153+
154+#
155+# Ubuntu Core Webapps
156+# Copyright (C) 2015 Canonical
157+#
158+# This program is free software: you can redistribute it and/or modify
159+# it under the terms of the GNU General Public License as published by
160+# the Free Software Foundation, either version 3 of the License, or
161+# (at your option) any later version.
162+#
163+# This program is distributed in the hope that it will be useful,
164+# but WITHOUT ANY WARRANTY; without even the implied warranty of
165+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
166+# GNU General Public License for more details.
167+#
168+# You should have received a copy of the GNU General Public License
169+# along with this program. If not, see <http://www.gnu.org/licenses/>.
170+#
171+
172+import contextlib
173+import logging
174+
175+import autopilot.logging
176+import ubuntuuitoolkit
177+from autopilot import platform
178+
179+logger = logging.getLogger(__name__)
180+
181+
182+class PageObject():
183+
184+ """Base page object."""
185+
186+ def __init__(self, proxy, driver):
187+ """Initialize the page object.
188+
189+ :param proxy: The autopilot application proxy.
190+ :param driver: The webdriver used to introspect and control the web
191+ container.
192+
193+ """
194+ super().__init__()
195+ self.proxy = proxy
196+ self.driver = driver
197+
198+ def get_container_proxy(self):
199+ return self.proxy.select_single('WebappContainerWebview')
200+
201+ def find_element_by_css_in_desktop_or_mobile(
202+ self, desktop_css_selector, mobile_css_selector):
203+ return self.driver.find_element_by_css_selector(
204+ '{}, {}'.format(desktop_css_selector, mobile_css_selector))
205+
206+ @contextlib.contextmanager
207+ def focused_type(self, input_web_element):
208+ """Type into an input web element.
209+
210+ This context manager takes care of making sure a particular
211+ *input_target* UI control is selected before any text is entered.
212+
213+ """
214+
215+ pointer = ubuntuuitoolkit.get_pointing_device()
216+ pointer.move(*self.get_center_point(input_web_element))
217+ pointer.click()
218+
219+ yield ubuntuuitoolkit.get_keyboard()
220+
221+ def get_center_point(self, web_element):
222+ web_element_center_x = web_element.location.get('x') + (
223+ web_element.size.get('width') // 2)
224+ web_element_center_y = web_element.location.get('y') + (
225+ web_element.size.get('height') // 2)
226+
227+ container_x, container_y, _, _ = self.get_container_proxy().globalRect
228+
229+ return (
230+ container_x + web_element_center_x,
231+ container_y + web_element_center_y
232+ )
233+
234+
235+class AmazonMainPage(PageObject):
236+
237+ """Page object for the main page of the Amazon website."""
238+
239+ @autopilot.logging.log_action(logger.info)
240+ def search(self, query):
241+ search_bar = self._get_search_bar()
242+ self._enter_search_query(search_bar, query)
243+ go_button = self._get_go_button(search_bar)
244+ go_button.click()
245+ return AmazonResultsPage(self.proxy, self.driver)
246+
247+ def _get_search_bar(self):
248+ desktop_css_selector = '#nav-searchbar'
249+ mobile_css_selector = '.nav-searchbar'
250+ return self.find_element_by_css_in_desktop_or_mobile(
251+ desktop_css_selector, mobile_css_selector)
252+
253+ def _enter_search_query(self, search_bar, query):
254+ desktop_css_selector = '#twotabsearchtextbox'
255+ mobile_css_selector = 'input.nav-input[type="text"]'
256+ search_text_field = self.find_element_by_css_in_desktop_or_mobile(
257+ desktop_css_selector, mobile_css_selector)
258+ if platform.model() == 'Desktop':
259+ with self.focused_type(search_text_field) as keyboard:
260+ keyboard.type(query)
261+ else:
262+ # The location returned by the driver in the phone is not accurate.
263+ # TODO report a bug.
264+ # XXX We can't send keys because of bug http://pad.lv/1421423
265+ # Remove the workaround once that bug is fixed.
266+ # --elopio - 2015-02-12
267+ # search_text_field.send_keys('Test')
268+ self.driver.execute_script(
269+ "document.getElementsByClassName("
270+ "'nav-searchbar')[0].getElementsByTagName("
271+ "'input')[1].setAttribute('value', '{}')".format(query)
272+ )
273+
274+ def _get_go_button(self, search_bar):
275+ desktop_css_selector = '.nav-submit-input'
276+ mobile_css_selector = '.nav-input'
277+ return self.find_element_by_css_in_desktop_or_mobile(
278+ desktop_css_selector, mobile_css_selector)
279+
280+
281+class AmazonResultsPage(PageObject):
282+
283+ """Page object for the main page of the Amazon Website."""
284+
285+ def get_search_results_count_label(self):
286+ desktop_css_selector = '#s-result-count'
287+ mobile_css_selector = '#results span'
288+ return self.find_element_by_css_in_desktop_or_mobile(
289+ desktop_css_selector, mobile_css_selector).text
290
291=== added file 'webapp-amazon/tests/autopilot/amazon_webapp/test_amazon_webapp.py'
292--- webapp-amazon/tests/autopilot/amazon_webapp/test_amazon_webapp.py 1970-01-01 00:00:00 +0000
293+++ webapp-amazon/tests/autopilot/amazon_webapp/test_amazon_webapp.py 2015-02-17 19:29:56 +0000
294@@ -0,0 +1,152 @@
295+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
296+
297+#
298+# Ubuntu Core Webapps
299+# Copyright (C) 2015 Canonical
300+#
301+# This program is free software: you can redistribute it and/or modify
302+# it under the terms of the GNU General Public License as published by
303+# the Free Software Foundation, either version 3 of the License, or
304+# (at your option) any later version.
305+#
306+# This program is distributed in the hope that it will be useful,
307+# but WITHOUT ANY WARRANTY; without even the implied warranty of
308+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
309+# GNU General Public License for more details.
310+#
311+# You should have received a copy of the GNU General Public License
312+# along with this program. If not, see <http://www.gnu.org/licenses/>.
313+#
314+
315+import os
316+import shutil
317+import subprocess
318+
319+from autopilot import (
320+ get_test_configuration,
321+ testcase
322+)
323+from selenium import webdriver
324+from selenium.webdriver.chrome import options
325+from ubuntuuitoolkit import fixture_setup
326+
327+import amazon_webapp
328+
329+
330+DESKTOP_FILE_NAME = 'webapp-amazon.desktop'
331+
332+DEFAULT_IMPLICIT_WAIT = 10 # seconds
333+
334+
335+def get_source_desktop_file_path():
336+ return os.path.join(
337+ os.path.dirname(amazon_webapp.__file__), '..', '..', '..',
338+ DESKTOP_FILE_NAME)
339+
340+
341+def get_local_desktop_file_directory():
342+ return os.path.join(
343+ os.environ.get('HOME'), '.local', 'share', 'applications')
344+
345+
346+def get_chrome_driver_path():
347+ env = os.environ.copy()
348+ readonly_dependencies_path = get_readonly_dependencies_path()
349+ if readonly_dependencies_path:
350+ # XXX When running with adt-run, this is needed if the image is
351+ # read-only so we can find the chromium driver path.
352+ env['PERL5LIB'] = os.path.join(
353+ readonly_dependencies_path, 'usr', 'share', 'perl5')
354+
355+ architecture = subprocess.check_output(
356+ ['dpkg-architecture', '-qDEB_HOST_MULTIARCH'],
357+ env=env, universal_newlines=True).strip()
358+
359+ chrome_driver_path = '/usr/lib/{}/oxide-qt/chromedriver'.format(
360+ architecture)
361+
362+ if readonly_dependencies_path and not os.path.exists(chrome_driver_path):
363+ # Try the read-only install path.
364+ chrome_driver_path = os.path.join(
365+ readonly_dependencies_path, chrome_driver_path.lstrip('/'))
366+
367+ return chrome_driver_path
368+
369+
370+def get_readonly_dependencies_path():
371+ adt_artifacts_path = os.environ.get('ADT_ARTIFACTS', None)
372+ if adt_artifacts_path:
373+ return os.path.join(os.path.dirname(adt_artifacts_path), 'deps')
374+ else:
375+ return None
376+
377+
378+class AmazonWebappTestCase(testcase.AutopilotTestCase):
379+
380+ DEFAULT_WEBVIEW_INSPECTOR_IP = '127.0.0.1'
381+ DEFAULT_WEBVIEW_INSPECTOR_PORT = 9221
382+
383+ def setUp(self):
384+ super().setUp()
385+ self.setup_webdriver_environment()
386+
387+ def setup_webdriver_environment(self):
388+ self.useFixture(fixture_setup.InitctlEnvironmentVariable(
389+ global_=True,
390+ UBUNTU_WEBVIEW_DEVTOOLS_HOST=self.DEFAULT_WEBVIEW_INSPECTOR_IP,
391+ UBUNTU_WEBVIEW_DEVTOOLS_PORT=str(
392+ self.DEFAULT_WEBVIEW_INSPECTOR_PORT)
393+ ))
394+
395+ def launch_application(self):
396+ if (get_test_configuration().get('from_source', None) and
397+ os.path.exists(get_source_desktop_file_path())):
398+ return self.launch_application_from_source()
399+ else:
400+ return self.launch_installed_application()
401+
402+ def launch_application_from_source(self):
403+ test_desktop_file_path = os.path.join(
404+ get_local_desktop_file_directory(), 'webapps-test.desktop')
405+ shutil.copy(get_source_desktop_file_path(), test_desktop_file_path)
406+ self.addCleanup(os.remove, test_desktop_file_path)
407+
408+ return self.launch_upstart_application('webapps-test')
409+
410+ def launch_installed_application(self):
411+ return self.launch_click_package(
412+ 'com.ubuntu.developer.webapps.webapp-amazon')
413+
414+ def get_webdriver(self):
415+ driver_options = options.Options()
416+ driver_options.binary_location = ''
417+ driver_options.debugger_address = '{}:{}'.format(
418+ self.DEFAULT_WEBVIEW_INSPECTOR_IP,
419+ self.DEFAULT_WEBVIEW_INSPECTOR_PORT)
420+ driver = webdriver.Chrome(
421+ executable_path=get_chrome_driver_path(),
422+ chrome_options=driver_options
423+ )
424+
425+ self.assertIsNot(driver, None)
426+
427+ self.addCleanup(self.close_webdriver, driver)
428+
429+ driver.implicitly_wait(DEFAULT_IMPLICIT_WAIT)
430+ return driver
431+
432+ def close_webdriver(self, driver):
433+ driver.close()
434+ driver.quit()
435+
436+ def test_search_in_amazon_must_show_results(self):
437+ proxy = self.launch_application()
438+ driver = self.get_webdriver()
439+
440+ amazon = amazon_webapp.AmazonMainPage(proxy, driver)
441+ results_page = amazon.search('Test')
442+
443+ results = results_page.get_search_results_count_label()
444+ self.assertTrue(
445+ results.endswith('results for "Test"') or
446+ results.endswith('Results'))

Subscribers

People subscribed via source and target branches