Merge lp:~nik90/ubuntu-clock-app/reorganise-stopwatch-tests into lp:ubuntu-clock-app/saucy

Proposed by Nekhelesh Ramananthan
Status: Merged
Approved by: Leo Arias
Approved revision: 360
Merged at revision: 355
Proposed branch: lp:~nik90/ubuntu-clock-app/reorganise-stopwatch-tests
Merge into: lp:ubuntu-clock-app/saucy
Diff against target: 300 lines (+132/-106)
3 files modified
stopwatch/LapList.qml (+1/-0)
tests/autopilot/ubuntu_clock_app/emulators.py (+82/-28)
tests/autopilot/ubuntu_clock_app/tests/test_stopwatch.py (+49/-78)
To merge this branch: bzr merge lp:~nik90/ubuntu-clock-app/reorganise-stopwatch-tests
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Leo Arias (community) code review Approve
Review via email: mp+208475@code.launchpad.net

Commit message

Refactor stopwatch helpers to follow the page object pattern.

Description of the change

Refactor stopwatch helpers to follow the page object pattern. Tested on both phone and tablet interface.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

The jenkins failure above is not a clock AP issue.

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

75 + def get_stopwatch_time(self):
76 + """Returns the stopwatch time label"""
77 + return self.wait_select_single('Label', objectName='label')

This is actually an implementation detail, the fact that the stop watch time is kept in a label called label. The service your UI is providing to the users is to give them the time of the stopwatch, so that's what we should return in the public helpers, instead of a label element.

So, this would be better:

def get_stopwatch_time(self):
    return self.wait_select_single('Label', objectName='label').text

That way, if you or a designer decide to show the time in a component called 'Timer' that has a 'time' property instead of a 'Label' component with a 'text' property, you will only have to change one line and all your tests will keep working.

Now, on:

83 + @autopilot_logging.log_action(logger.info)
84 + def get_stopwatch_lap(self, index):

What you want is the element so you can later delete it, right?
So, this method should be kept private, to be sure that it's not in use by any tests in case you need to refactor it.
I'd say _get_stopwatch_lap_list_element is a better name.

And about the log_action decorator, what's nice is to put it only on the actions a user does. Like go_to_some_page, or save_something, or delete_something. We don't really need to log any method that starts with get_something, because they are only used as lower level helpers to perform an action, or as things that you would assert on a test. I'm not sure if I'm being clear, if not, let me know on the chat. Also, feel free to disagree, this is not an exact science and we are just inventing things here :)

93 + def start_stopwatch(self):
107 + def reset_stopwatch(self):
117 + def create_lap(self):

These need a log_action ^

A small detail is that the docstring of methods "prescribes the function or method's effect as a command", so instead of a verb like Returns, it should be Return. And instead of "Function to stop..." it should be "Stop..."
This comes from pep257. http://legacy.python.org/dev/peps/pep-0257/

review: Needs Fixing
356. By Nekhelesh Ramananthan

Returned proper time instead of label

357. By Nekhelesh Ramananthan

Added autopilot loggers to the public functions and renamed get_stopwatch_lap function

358. By Nekhelesh Ramananthan

Fixed comments according to pep257 standards

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

227 + def test_start_button_must_start_stopwatch(self):
234 + def test_stop_button_must_stop_stopwatch(self):

By looking just at the UI, there isn't really a start and a stop button. Well, they are there, but that's an implementation detail that we know because we have read the code.

From a user story point of view, I think this would be clearer named as something like:

test_click_clock_center_must_start_stopwatch(self)
test_click_clock_center_with_stop_watch_started_must_stop_it(self)

If that last name makes the line longer than 80 characters, then it could be just:
test_click_clock_center_must_stop_stopwatch(self):

240 + self.assertThat(
241 + stopwatch_time,
242 + NotEquals(self.page.get_stopwatch_time().text))

I think that one is not necessary because in essence it would be the same check we are doing on the previous tests, I think.
What if we do something like this:

stopwatch_time_after = lambda: self.page.get_stopwatch_time()
time.sleep(1)
self.assertThat(
   stopwatch_time_after,
   Equals(self.page.get_stopwatch_time())))

The eventually is not needed in there, btw.

Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

>
> So, this would be better:
>
> def get_stopwatch_time(self):
> return self.wait_select_single('Label', objectName='label').text
>
> That way, if you or a designer decide to show the time in a component called
> 'Timer' that has a 'time' property instead of a 'Label' component with a
> 'text' property, you will only have to change one line and all your tests will
> keep working.
>

I actually had in mind, but forgot it at the last second :)

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
359. By Nekhelesh Ramananthan

Clarified test names and fixed failing test

360. By Nekhelesh Ramananthan

Fixed stop stopwatch test

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

I like this so much, I'll print it ;)

review: Approve (code review)
Revision history for this message
Nekhelesh Ramananthan (nik90) wrote :

;)

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'stopwatch/LapList.qml'
2--- stopwatch/LapList.qml 2014-02-18 20:09:55 +0000
3+++ stopwatch/LapList.qml 2014-02-27 00:04:13 +0000
4@@ -52,6 +52,7 @@
5
6 ListView {
7 id: listViewLap
8+ objectName: "listStopwatchLaps"
9
10 clip: true
11 anchors { left: parent.left; right: parent.right; }
12
13=== modified file 'tests/autopilot/ubuntu_clock_app/emulators.py'
14--- tests/autopilot/ubuntu_clock_app/emulators.py 2014-02-26 21:06:47 +0000
15+++ tests/autopilot/ubuntu_clock_app/emulators.py 2014-02-27 00:04:13 +0000
16@@ -55,6 +55,16 @@
17 self.switch_to_tab('AlarmTab')
18 return self.wait_select_single(AlarmPage)
19
20+ @autopilot_logging.log_action(logger.info)
21+ def open_stopwatch(self):
22+ """Open the Stopwatch Tab.
23+
24+ :return the Stopwatch page.
25+
26+ """
27+ self.switch_to_tab('StopwatchTab')
28+ return self.wait_select_single(StopwatchPage)
29+
30 # Common Emulator Functions
31 def _drag_page(self, page, direction):
32 """Function to drag the page up/down"""
33@@ -141,34 +151,6 @@
34 return self.wait_select_single(
35 "Standard", objectName="savedworldcity0")
36
37- # STOPWATCH Page Emulator Functions
38- def get_stopwatch_label(self):
39- """Returns the select for the stopwatch label"""
40- return self.select_single(
41- "StopwatchPage").wait_select_single("Label", objectName="label")
42-
43- def get_stopwatch_button(self):
44- """Returns the select for the stopwatch button"""
45- return self.wait_select_single(
46- "StopwatchFace", objectName="stopwatchFace")
47-
48- def get_lap_button(self):
49- """Returns the select for the lap button"""
50- return self.wait_select_single("ImageButton", objectName="lapButton")
51-
52- def get_reset_button(self):
53- """Returns the select for the reset button"""
54- return self.wait_select_single("ImageButton", objectName="resetButton")
55-
56- def get_num_of_laps(self):
57- """Returns the number of laps in the stopwatch page."""
58- stopwatch_page = self.select_single("StopwatchPage")
59- return int(stopwatch_page.wait_select_single("QQuickListView").count)
60-
61- def get_first_laps_list_item(self):
62- """Returns the first lap list item in the stopwatch page."""
63- return self.wait_select_single("Standard", objectName="laps0")
64-
65
66 class LabelDots(toolkit_emulators.TextField):
67 """Autopilot helper for the LabelDots component."""
68@@ -214,6 +196,78 @@
69 objectName='animationContainer').moving.wait_for(False)
70
71
72+class StopwatchPage(Page):
73+ """Autopilot helper for the Stopwatch page."""
74+
75+ def get_stopwatch_time(self):
76+ """Return the stopwatch time"""
77+ return self.wait_select_single('Label', objectName='label').text
78+
79+ def get_num_of_laps(self):
80+ """Return the number of laps in the stopwatch page."""
81+ return int(self.wait_select_single('QQuickListView').count)
82+
83+ def _get_stopwatch_lap_list_element(self, index):
84+ """Return a lap list item in the stopwatch page.
85+
86+ :param index: stopwatch lap number
87+
88+ """
89+ return self.wait_select_single(
90+ 'Standard', objectName='laps{}'.format(index))
91+
92+ @autopilot_logging.log_action(logger.info)
93+ def start_stopwatch(self):
94+ """Start the stopwatch"""
95+ self._click_start_stop_stopwatch_button()
96+
97+ @autopilot_logging.log_action(logger.info)
98+ def stop_stopwatch(self):
99+ """Stop the stopwatch"""
100+ self._click_start_stop_stopwatch_button()
101+
102+ def _click_start_stop_stopwatch_button(self):
103+ """Click the start/stop stopwatch button"""
104+ start_stop_button = self.wait_select_single(
105+ 'StopwatchFace', objectName='stopwatchFace')
106+ self.pointing_device.click_object(start_stop_button)
107+
108+ @autopilot_logging.log_action(logger.info)
109+ def reset_stopwatch(self):
110+ """Reset the stopwatch"""
111+ self._click_reset_stopwatch_button()
112+
113+ def _click_reset_stopwatch_button(self):
114+ """Click the reset stopwatch button"""
115+ reset_button = self.wait_select_single(
116+ 'ImageButton', objectName='resetButton')
117+ self.pointing_device.click_object(reset_button)
118+
119+ @autopilot_logging.log_action(logger.info)
120+ def create_lap(self):
121+ """Create a stopwatch lap"""
122+ self._click_lap_stopwatch_button()
123+
124+ def _click_lap_stopwatch_button(self):
125+ """Click the stopwatch lap button"""
126+ lap_button = self.wait_select_single(
127+ 'ImageButton', objectName='lapButton')
128+ self.pointing_device.click_object(lap_button)
129+
130+ @autopilot_logging.log_action(logger.info)
131+ def delete_lap(self, index):
132+ """Delete stopwatch lap
133+
134+ :param index: stopwatch lap number
135+
136+ """
137+ lap = self._get_stopwatch_lap_list_element(index=index)
138+ if not self.main_view.wideAspect:
139+ self.drag_page_up()
140+ lap.swipe_to_delete()
141+ lap.confirm_removal()
142+
143+
144 class AlarmPage(Page):
145 """Autopilot helper for the Alarm page."""
146
147
148=== modified file 'tests/autopilot/ubuntu_clock_app/tests/test_stopwatch.py'
149--- tests/autopilot/ubuntu_clock_app/tests/test_stopwatch.py 2014-02-19 12:03:16 +0000
150+++ tests/autopilot/ubuntu_clock_app/tests/test_stopwatch.py 2014-02-27 00:04:13 +0000
151@@ -20,100 +20,71 @@
152
153 from __future__ import absolute_import
154
155-from testtools.matchers import Equals, NotEquals, Is, Not
156+import time
157+from testtools.matchers import Equals, NotEquals
158 from autopilot.matchers import Eventually
159
160 from ubuntu_clock_app.tests import ClockAppTestCase
161
162+
163 class TestStopwatch(ClockAppTestCase):
164 """Tests the stopwatch page features"""
165
166- """ This is needed to wait for the application to start.
167- In the testfarm, the application may take some time to show up."""
168 def setUp(self):
169+ """ This is needed to wait for the application to start.
170+
171+ In the testfarm, the application may take some time to show up.
172+
173+ """
174 super(TestStopwatch, self).setUp()
175 self.assertThat(
176 self.main_view.visible, Eventually(Equals(True)))
177
178- # Move to stopwatch tab
179- self.main_view.switch_to_tab("StopwatchTab")
180-
181- def tearDown(self):
182- super(TestStopwatch, self).tearDown()
183-
184- def create_lap(self):
185- """Function to create a stopwatch lap"""
186- lap_button = self.main_view.get_lap_button()
187- self.pointing_device.click_object(lap_button)
188-
189- def start_stop_stopwatch(self):
190- """Function to start/stop the stopwatch"""
191- start_stop_button = self.main_view.get_stopwatch_button()
192- self.pointing_device.click_object(start_stop_button)
193-
194- def test_start_stop_reset_stopwatch(self):
195- """Test to check the proper functioning of the start/stop/reset of stopwatch"""
196-
197- stopwatch_label = self.main_view.get_stopwatch_label()
198- reset_button = self.main_view.get_reset_button()
199-
200- # Start the stopwatch and check if the stopwatch starts ticking
201- stopwatch_label_before = self.main_view.get_stopwatch_label().text
202- self.start_stop_stopwatch()
203- self.assertThat(stopwatch_label.text, Eventually(NotEquals("00:00.0")))
204-
205- # Stop the stopwatch and check it has indeed stopped
206- self.start_stop_stopwatch()
207- self.assertThat(stopwatch_label_before, NotEquals(self.main_view.get_stopwatch_label().text))
208- stopwatch_label_after = lambda: self.main_view.get_stopwatch_label().text
209- self.assertThat(stopwatch_label_after, Eventually(Equals(self.main_view.get_stopwatch_label().text)))
210-
211- # Press reset and check if the stopwatch has been resetted
212- self.pointing_device.click_object(reset_button)
213- self.assertThat(stopwatch_label.text, Eventually(Equals("00:00.0")))
214-
215- def test_create_lap(self):
216- """Test to check creating a new stopwatch lap"""
217-
218- # Start the stopwatch
219- stopwatch_label = self.main_view.get_stopwatch_label().text
220- self.start_stop_stopwatch()
221-
222- # Ensure that the stopwatch actually ticks
223- self.assertThat(stopwatch_label, Eventually(NotEquals("00:00.0")))
224-
225- # Create a stopwatch lap by pressing the lap button
226- num_of_laps_old = self.main_view.get_num_of_laps()
227- self.create_lap()
228-
229+ self.page = self.main_view.open_stopwatch()
230+
231+ def test_click_clock_center_must_start_stopwatch(self):
232+ """Test if start button starts the stopwatch"""
233+ stopwatch_time = self.page.get_stopwatch_time()
234+ self.page.start_stopwatch()
235+ # Check the stopwatch started running
236+ self.assertThat(stopwatch_time, Eventually(NotEquals('00:00.0')))
237+
238+ def test_click_clock_center_with_stopwatch_started_must_stop_it(self):
239+ """Test if stop button stops the stopwatch"""
240+ self.page.start_stopwatch()
241+ self.page.stop_stopwatch()
242+ # Check that the stopwatch has stopped
243+ stopwatch_time = self.page.get_stopwatch_time()
244+ time.sleep(1)
245+ self.assertThat(
246+ stopwatch_time,
247+ Equals(self.page.get_stopwatch_time()))
248+
249+ def test_restart_button_must_restart_stopwatch_time(self):
250+ """Test if reset button resets the stopwatch time"""
251+ self.page.start_stopwatch()
252+ stopwatch_time = self.page.get_stopwatch_time()
253+ self.page.reset_stopwatch()
254+ # Check that the stopwatch has been reset
255+ self.assertThat(stopwatch_time, Eventually(Equals('00:00.0')))
256+
257+ def test_lap_button_must_create_stopwatch_lap(self):
258+ """Test if lap button creates a stopwatch lap"""
259+ self.page.start_stopwatch()
260+ num_of_laps_old = self.page.get_num_of_laps()
261+ self.page.create_lap()
262 # Confirm that the lap is created
263 self.assertThat(
264- self.main_view.get_num_of_laps,
265+ self.page.get_num_of_laps,
266 Eventually(Equals(num_of_laps_old + 1)))
267
268- def test_delete_lap(self):
269- """Test to check deleting a stopwatch lap"""
270-
271- # Start the stopwatch
272- stopwatch_label = self.main_view.get_stopwatch_label().text
273- self.start_stop_stopwatch()
274-
275- # Ensure that the stopwatch actually ticks
276- self.assertThat(stopwatch_label, Eventually(NotEquals("00:00.0")))
277-
278- # Create a stopwatch lap
279- self.create_lap()
280-
281- num_of_laps_old = self.main_view.get_num_of_laps()
282- if not(self.main_view.wideAspect):
283- self.main_view.drag_page_up("StopwatchPage")
284-
285- # Delete stopwatch lap
286- first_lap = self.main_view.get_first_laps_list_item()
287- first_lap.swipe_to_delete()
288- first_lap.confirm_removal()
289-
290+ def test_delete_lap_must_delete_from_stopwatch_lap_list(self):
291+ """Test to delete a lap and check if it has been removed properly"""
292+ self.page.start_stopwatch()
293+ self.page.create_lap()
294+ num_of_laps_old = self.page.get_num_of_laps()
295+ self.page.delete_lap(index=0)
296 # Check that the stopwatch lap has been deleted
297 self.assertThat(
298- self.main_view.get_num_of_laps,
299+ self.page.get_num_of_laps,
300 Eventually(Equals(num_of_laps_old - 1)))

Subscribers

People subscribed via source and target branches