[18:41] <svij> balloons: hey, I tried to write my first autopilot test, but I'm struggling… I tried to write a test for this bug: https://bugs.launchpad.net/ubuntu-calculator-app/+bug/1410986 But I don't know how to access the calculationHistory to do a "longpress"
[18:43] <balloons> svij, howdy
[18:43] <balloons> let's have a look
[18:44] <svij> :)
[18:52] <balloons> svij, so I believe step 2 is basically press and hold on the white screen at the top
[18:52] <balloons> it will let you play with the calculations
[18:53] <svij> balloons: yes, and I have no idea how to do that
[18:53] <balloons> you should get an action menu and checkboxes..
[18:53] <balloons> ohh, lol, you mean have AP do that :p
[18:53] <svij> yeah :D
[18:53] <svij> sorry. :D
[18:53] <balloons> svij,are you able to find the object ok?
[18:54] <balloons> we can press and hold using https://developer.ubuntu.com/api/autopilot/python/1.5.0/autopilot.input.Touch/
[18:55] <svij> balloons: I'm not sure which object it is (I'm not really familiar with qml)
[18:56] <balloons> svij, awesome, so let's talk about how to figure that out
[18:56] <balloons> svij, have you tried using autopilot vis yet? Because that's how we'll figure out the object
[18:56] <svij> hm, nope
[18:57] <svij> let me find the docs…
[18:58] <svij> https://developer.ubuntu.com/api/autopilot/python/1.5.0/guides-running_ap/#visualise-introspection-tree the images are broken :(
[18:59] <balloons> svij, yikes.. we'll have to fix that!
[18:59] <svij> (it's also broken on a couple of other pages of the autopilot pages)
[18:59] <balloons> right, thanks for pointing it out. I'll file a bug on the site nd get it fixed
[19:01] <balloons> anyways, so right. first lanuch the app, then run vis to have a visual look at the introspection tree
[19:01] <balloons> for calculator I wold do this:
[19:01] <balloons> autopilot launch -i Qt qmlscene app/ubuntu-calculator-app.qml
[19:01] <balloons> autopilot3 vis
[19:03] <svij> got it
[19:03] <svij> and selected the calculator app in vis
[19:04] <balloons> excellent. So now you can see the full tree. It's a little odd to browse at first, but you'll get used to it
[19:05] <balloons> look under the MainView and OrientationHelper to get into the app internals
[19:05] <svij> and then?
[19:06] <balloons> see the yellow button next to the spyglass at the top of the app?
[19:06] <svij> yes
[19:06] <balloons> if you toggle it, it would highlight the part of the app that the object you select in the tree represents
[19:06] <balloons> this can sometimes be useful
[19:06] <balloons> so now I would try and figure out / understand what objects are in the top of the calculator
[19:08] <balloons> looking through things, I see a PageStack, other Page definitions, and then finally a header and a scrollable view
[19:10] <svij> the scrollview was hard to find
[19:10] <svij> scrollable view*
[19:10] <balloons> svij, yes, sometimes the object tree is just a little confusing. In those cases, let me show you another way to grok the tree
[19:12] <svij> and the QQuickLoader seems to be one item of the calculation history
[19:12] <balloons> check out print_tree() on https://developer.ubuntu.com/api/autopilot/python/1.5.0/autopilot.introspection.ProxyBase/
[19:12] <balloons> so one thing you can do is launch the app under test in python, then do a dump of the tree if you will to a file, using print_tree on the root node
[19:12] <balloons> does that make sense?
[19:13] <svij> how do I "launch the app under test in python"?
[19:15] <balloons> svij, that's what is happening in the test cases. So in this case I would add another test case to the test_main.py file, and then have autopilot run it
[19:15] <svij> oh right
[19:15] <balloons> svij, something tiny, like def test_dump_tree(self):
[19:15] <balloons>         self.app.main_view.print_tree()
[19:16] <svij> trying…
[19:16] <balloons> make sure you redirect the output to a file when you run :-)
[19:17] <balloons> so it doesn't dump to your console
[19:18] <svij> hehe, yes.
[19:19] <svij> okay, and now?
[19:20] <balloons> we can review the output. Also, I had a look in the test helpers and noticed there is already a helper for calculationhistory
[19:20] <balloons> do you see it in __init__.py?
[19:20] <svij> yes
[19:22] <balloons> I also see mainview has a helper method already called get_history
[19:22] <svij> yes, i see
[19:23] <balloons> if you look inside your dump, you can see this object.. Look for objectName: 'scrollableView'
[19:23] <balloons> anyways, I might suggest that this is exactly what we want to press and hold
[19:23] <balloons> what do you think?
[19:23] <svij> sure
[19:25] <svij> but do I need to press and hold that scrollableView or one item in that scrollableView?
[19:25] <balloons> so let's try. First, use the helper method that already exists to get the object, then use autopilot to press and hold it
[19:26] <balloons> when you interact with an object, by default autopilot will try and determine it's coordinates, then press in the middle of the object
[19:26] <svij> ahh
[19:26] <balloons> so that might work just fine for us
[19:26] <svij> do I need the "press_and_hold" method from MainView in __init__.py?
[19:29] <balloons> svij, no, that's a custom method and by looking at it I can see it's intended to press and hold a button on the keypad
[19:29] <svij> oh right
[19:29] <balloons> yea, should be more aptly named / commented
[19:30] <svij> so, I'm getting the object with "self.app.main_view.get_history()" right?
[19:31] <balloons> anyways, so just use autopilot methods themselves to click.
[19:31] <balloons> svij, yes
[19:31] <svij> so, call ".press()" on that?
[19:32] <svij> or tap, with a duration…
[19:32] <balloons> svij, yes but I realized that doesn't help you :-) Have a look at https://developer.ubuntu.com/api/autopilot/python/1.5.0/autopilot.input.Pointer/.. You'll see there's a nice method called click_object
[19:32] <balloons> and move_to_object
[19:33] <balloons> otherwise you'd have to read the object properties yourself, then press at the proper position. Easier to simply use the built-in method AP provides to do this
[19:35] <svij> wait, how do I call that exactly?
[19:36] <balloons> svij, so notice you'll need to creating a pointing device, and these methods are for that class. Now, if you look at the CalculatorApp class in __init__.py, you'll see we've done this in the __init__ method
[19:37] <balloons> err sorry.. we declare a property in the class rather . . .
[19:37] <svij> um, ok.
[19:37] <balloons> does that make sense to you?
[19:38] <balloons> just know you have a pointing_device ready to go you can use for this stuff under self.app.pointing_device
[19:38] <balloons> so self.app.pointing_device.click_object, self.app.pointing_device.press, etc
[19:39] <svij> let me check…
[19:39] <balloons> you can learn more about the details of how the app is launched and the objects initialized can come later
[19:41] <svij> I've got two lines in my test case right now:
[19:42] <svij>         self.app.pointing_device.move_to_object(self.app.main_view.get_history())
[19:42] <svij>         self.app.pointing_device.click_object(self)
[19:42] <svij> but that doesn't work
[19:43] <svij> "ValueError: Object '<ubuntu_calculator_app.CalculationHistory object at 0x7f2368163c18>' does not have any recognised position attributes
[19:43] <svij> oh wait…
[19:44] <balloons> svij, just fyi the self.app.pointing_device.move_to_object isn't needed as click_object will do that for you
[19:44] <balloons> svij, so here's what I wrote:
[19:44] <balloons> history = self.app.main_view.get_history()
[19:44] <balloons> self.app.pointing_device.click_object(history, 1, 3)
[19:45] <balloons> I got the same error as you, so clearly the object we get from history isn't what is being shown :-)
[19:45] <balloons> since it doesn't have x,y,z coords, it's an internal object.
[19:45] <svij> okay
[19:46] <balloons> ohh, look at the get_history method again
[19:47] <balloons> it's returning a class
[19:48] <svij> yes
[19:48] <svij> so…?
[19:49] <balloons> so, we need an object to pass to AP, not a class
[19:49] <svij> right
[19:50] <balloons> svij, so notice in __init__, it puts the object in self.app. So we could pass AP the object directly
[19:51] <balloons> history.app. That runs for me, but doesn't quite do what we want
[19:51] <svij> let me check…
[19:51] <balloons> I hope working through it like this is helping.. I'm going through it slowly with you the same way as I would approach it
[19:51] <svij> yeah, sure, it helps. :)
[19:53] <svij> and yes, it doesn't really help us.
[19:53] <svij> (i mean the code)
[19:54] <balloons> but you now have the knowledge you need to click the object once we find it
[19:55] <balloons> so at this point I would look deeper into scrollable view. It looks like / my guess is the history we see is laid out in rows
[19:55] <svij> it is
[19:56] <svij> QQuickLoaders it seems
[19:57] <balloons> so ideally we would enter a calculation, then long press on it's history result I think
[19:57] <balloons> or actually it's asking us to test deleting more than 1 as well
[19:59] <svij> yes
[19:59] <svij> but I need to figure out how to access one of those QQuickloaders
[19:59] <balloons> svij, so one thing we can do is use strings to help get us closer to understanding the layout. I would do a print_tree again, but using the calc history object as the root this time.
[20:00] <balloons> Then I would look for a string that is one of the numbers in the history
[20:01] <balloons> doing that gave me an object under /comubuntucalculator/QQuickView/MainView/OrientationHelper/QQuickItem/QQuickItem/PageStack/PageWrapper/PageWithBottomEdge/ScrollableView/QQuickItem/QQuickColumn/QQuickLoader/Screen/QQuickRectangle/QQuickItem/QQuickColumn/QQuickRow/QQuickText
[20:03] <svij> yes
[20:04] <balloons> so at this point, it seems the layout is under Screen. I would go look at the qml and set an objectname on the object I want, if there isn't one
[20:05] <svij> ok, let me check…
[20:07] <svij> balloons: is "screenDelegate" as an objectName okay?
[20:07] <balloons> svij, sure
[20:07] <svij> good :)
[20:08] <svij> and now?
[20:09] <svij> I think I need to add an "_get_screen" function, which returns the screen?
[20:09] <svij> in __init__.py
[20:11] <balloons> svij, sure we could make a helper class with some methods again
[20:11] <balloons> but I'd get things working first before trying to abstract things
[20:11] <balloons> imho
[20:11] <svij> oh, right, sure.
[20:13] <svij> let me try…
[20:15] <balloons> k, I'm looking at / in Screen.qml
[20:15] <balloons> I added objectnames to the results and to the root Screen
[20:15]  * svij thought that this is a simple autopilot test for the beginning :)
[20:16] <balloons> svij, it's turning a little more difficult because we don't have handy access to the objects we need
[20:16] <balloons> but this is good learning material ;-)
[20:16] <svij> yeah, definitely
[20:17] <svij> isn't that documented somewhere?
[20:17] <balloons> what documented?
[20:18] <svij> how to find and access objects without helper functions
[20:19] <balloons> well, that's more or less what vis and print_tree are for. But there's no magical shortcuts besides simply reading the qml
[20:19] <svij> oh ok
[20:19] <balloons> that said, I'm happy to hear of docs that would be helpful. Happy to write them
[20:20] <svij> so the result text has objectName
[20:20] <svij> but I added an objectname to the root element
[20:21] <svij> (and I'm happy to help to improve the docs ;) )
[20:24] <balloons> :-)
[20:24] <balloons> So I noticed Screen.qml uses ListItemWithActions.qml
[20:24] <svij> yes
[20:25] <balloons> so when I added an objectname there, I see it now :-)
[20:25] <balloons> it's a bit odd
[20:25] <svij> wait, where do you "see it now"?
[20:26] <balloons> in the dbus tree dumps. I was looking for an objectname under a Screen object
[20:27] <svij> dbus tree dump was that print_tree() command, right?
[20:28] <balloons> svij, yes, I've been iterating over running dumps using it..
[20:28] <svij> ok
[20:28] <balloons> I'm dumping with Screen as the root object
[20:29] <balloons> note, there appears to be 2 Screen objects also
[20:29] <balloons> so
[20:29] <balloons>         screens = self.app.main_view.select_many('Screen')
[20:29] <balloons>         for screen in screens:
[20:29] <balloons>             screen.print_tree()
[20:31] <svij> ok
[20:31] <balloons> so, my issue to solve at this point is figure out how to make sure I select a specific object in the list. Doing those dumps shows me there are mutliple result objects for instance.. each result is sharing the same objectname
[20:32] <balloons> normally we would fix that by adding a dynamic property to the objectname declaration. So for example, objectName: "listitem" + index
[20:33] <svij> and in this case (if its not 'normally')?
[20:37] <svij> balloons: do you mind, if we continue tomorrow? It's getting late here and I'm really tired now and less concentrated…
[20:37] <balloons> svij, well the qml object is custom it seems. So it's more painful
[20:37] <balloons> svij, no worries. I'll figure out how to assign the objectname sanely so we can get back to the actual test
[20:37] <svij> great!
[20:37] <balloons> if you encounter something like this, the answer is always ask the developer
[20:38] <balloons> what on earth is going on in your custom object :-)
[20:38] <svij> first I need to figure out what are "normal" and what are "custom" objects
[20:38] <svij> and how to access them properly ;)
[20:38] <balloons> svij, "normal" is assign the objectname and then see it in the tree and go
[20:38] <balloons> lol
[20:39] <svij> anyway, thanks so far! I'll ping you tomorrow
[20:40] <balloons> good night!
[20:41] <svij> thanks
[20:41] <balloons> yw