GithubHelp home page GithubHelp logo

uiautomator's Introduction

uiautomator

Join the chat at https://gitter.im/xiaocong/uiautomator

MIT License build Coverage Status pypi downloads

This module is a Python wrapper of Android uiautomator testing framework. It works on Android 4.1+ (API Level 16~30) simply with Android device attached via adb, no need to install anything on Android device.

from uiautomator import device as d

d.screen.on()
d(text="Clock").click()

Installation

$ pip install uiautomator

Pre-requirements

  • Install Android SDK, and set ANDROID_HOME environment to the correct path.
  • Enable ADB setting on device and connect your android device using usb with your PC.
  • Allow apps to install from unknown sources on device settings.

import uiautomator

  • If ANDROID_SERIAL is defined in environment, or there is only one device connected:

    from uiautomator import device as d
  • Speficy the serial number when retrieving the device object

    from uiautomator import Device
    
    d = Device('014E05DE0F02000E')
  • Speficy the adb server host and port running on other computer

    Although adb supports -a option since SDK 4.3, but now it has a bug on it. The only way to start adb server listenning on all interfaces instead of localhost, is adb -a -P 5037 fork-server server &

    from uiautomator import Device
    
    d = Device('014E05DE0F02000E', adb_server_host='192.168.1.68', adb_server_port=5037)

Notes: In below examples, we use d represent the android device object.

Table of Contents

Basic API Usages

Watcher introduction

Handler introduction

Selector introduction

Basic API Usages

This part show the normal actions of the device through some simple examples.

  • Retrieve the device info

    d.info

    Below is a possible result:

    { u'displayRotation': 0,
      u'displaySizeDpY': 640,
      u'displaySizeDpX': 360,
      u'currentPackageName': u'com.android.launcher',
      u'productName': u'takju',
      u'displayWidth': 720,
      u'sdkInt': 18,
      u'displayHeight': 1184,
      u'naturalOrientation': True
    }
    

Key Event Actions of the device

  • Turn on/off screen

    # Turn on screen
    d.screen.on()
    # Turn off screen
    d.screen.off()

    Alternative method is:

    # wakeup the device
    d.wakeup()
    # sleep the device, same as turning off the screen.
    d.sleep()
  • Check if the screen is on or off

    if d.screen == "on":  # of d.screen != "off"
        # do something in case of screen on
        pass
    if d.screen == "off":  # of d.screen != "on"
        # do something in case of screen off
        pass
  • Press hard/soft key

    # press home key
    d.press.home()
    # press back key
    d.press.back()
    # the normal way to press back key
    d.press("back")
    # press keycode 0x07('0') with META ALT(0x02) on
    d.press(0x07, 0x02)
  • Next keys are currently supported:

    • home
    • back
    • left
    • right
    • up
    • down
    • center
    • menu
    • search
    • enter
    • delete(or del)
    • recent(recent apps)
    • volume_up
    • volume_down
    • volume_mute
    • camera
    • power

    You can find all key code definitions at Android KeyEvent.

Gesture interaction of the device

  • Click the screen

    # click (x, y) on screen
    d.click(x, y)
  • Long click the screen

    # long click (x, y) on screen
    d.long_click(x, y)
  • Swipe

    # swipe from (sx, sy) to (ex, ey)
    d.swipe(sx, sy, ex, ey)
    # swipe from (sx, sy) to (ex, ey) with 10 steps
    d.swipe(sx, sy, ex, ey, steps=10)
  • Drag

    # drag from (sx, sy) to (ex, ey)
    d.drag(sx, sy, ex, ey)
    # drag from (sx, sy) to (ex, ey) with 10 steps
    d.drag(sx, sy, ex, ey, steps=10)

Screen Actions of the device

  • Retrieve/Set Orientation

    The possible orientation is:

    • natural or n
    • left or l
    • right or r
    • upsidedown or u (can not be set)
    # retrieve orientation, it may be "natural" or "left" or "right" or "upsidedown"
    orientation = d.orientation
    # set orientation and freeze rotation.
    # notes: "upsidedown" can not be set until Android 4.3.
    d.orientation = "l" # or "left"
    d.orientation = "r" # or "right"
    d.orientation = "n" # or "natural"
  • Freeze/Un-Freeze rotation

    # freeze rotation
    d.freeze_rotation()
    # un-freeze rotation
    d.freeze_rotation(False)
  • Take screenshot

    # take screenshot and save to local file "home.png", can not work until Android 4.2.
    d.screenshot("home.png")
  • Dump Window Hierarchy

    # dump the widown hierarchy and save to local file "hierarchy.xml"
    d.dump("hierarchy.xml")
    # or get the dumped content(unicode) from return.
    xml = d.dump()
  • Open notification or quick settings

    # open notification, can not work until Android 4.3.
    d.open.notification()
    # open quick settings, can not work until Android 4.3.
    d.open.quick_settings()
  • Wait for idle or window update

    # wait for current window to idle
    d.wait.idle()
    # wait until window update event occurs
    d.wait.update()

Watcher

You can register watcher to perform some actions when a selector can not find a match.

  • Register Watcher

    When a selector can not find a match, uiautomator will run all registered watchers.

    • Click target when conditions match
    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \
                                 .click(text="Force Close")
    # d.watcher(name) ## creates a new named watcher.
    #  .when(condition)  ## the UiSelector condition of the watcher.
    #  .click(target)  ## perform click action on the target UiSelector.
    • Press key when conditions match
    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \
                                 .press.back.home()
    # Alternative way to define it as below
    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") \
                                 .press("back", "home")
    # d.watcher(name) ## creates a new named watcher.
    #  .when(condition)  ## the UiSelector condition of the watcher.
    #  .press.<keyname>.....<keyname>.()  ## press keys one by one in sequence.
    #  Alternavie way defining key sequence is press(<keybname>, ..., <keyname>)
  • Check if the named watcher triggered

    A watcher is triggered, which means the watcher was run and all its conditions matched.

    d.watcher("watcher_name").triggered
    # true in case of the specified watcher triggered, else false
  • Remove named watcher

    # remove the watcher
    d.watcher("watcher_name").remove()
  • List all watchers

    d.watchers
    # a list of all registered wachers' names
  • Check if there is any watcher triggered

    d.watchers.triggered
    #  true in case of any watcher triggered
  • Reset all triggered watchers

    # reset all triggered watchers, after that, d.watchers.triggered will be false.
    d.watchers.reset()
  • Remvoe watchers

    # remove all registered watchers
    d.watchers.remove()
    # remove the named watcher, same as d.watcher("watcher_name").remove()
    d.watchers.remove("watcher_name")
  • Force to run all watchers

    # force to run all registered watchers
    d.watchers.run()

Handler

The functionality of handler is same as Watcher, except it is implemented ourside of Android uiautomator. The most different usage between handler and watcher is, handler can use customized callback function.

def fc_close(device):
  if device(text='Force Close').exists:
    device(text='Force Close').click()
  return True  # return True means to break the loop of handler callback functions.

# turn on the handler callback function
d.handlers.on(fc_close)

# turn off the handler callback function
d.handlers.off(fc_close)

Selector

Selector is to identify specific ui object in current window.

# To seleted the object ,text is 'Clock' and its className is 'android.widget.TextView'
d(text='Clock', className='android.widget.TextView')

Selector supports below parameters. Refer to UiSelector java doc for detailed information.

  • text, textContains, textMatches, textStartsWith
  • className, classNameMatches
  • description, descriptionContains, descriptionMatches, descriptionStartsWith
  • checkable, checked, clickable, longClickable
  • scrollable, enabled,focusable, focused, selected
  • packageName, packageNameMatches
  • resourceId, resourceIdMatches
  • index, instance

Child and sibling UI object

  • child

    # get the child or grandchild
    d(className="android.widget.ListView").child(text="Bluetooth")
  • sibling

    # get sibling or child of sibling
    d(text="Google").sibling(className="android.widget.ImageView")
  • child by text or description or instance

    # get the child match className="android.widget.LinearLayout"
    # and also it or its child or grandchild contains text "Bluetooth"
    d(className="android.widget.ListView", resourceId="android:id/list") \
     .child_by_text("Bluetooth", className="android.widget.LinearLayout")
    
    # allow scroll search to get the child
    d(className="android.widget.ListView", resourceId="android:id/list") \
     .child_by_text(
        "Bluetooth",
        allow_scroll_search=True,
        className="android.widget.LinearLayout"
      )
    • child_by_description is to find child which or which's grandchild contains the specified description, others are the same as child_by_text.

    • child_by_instance is to find child which has a child UI element anywhere within its sub hierarchy that is at the instance specified. It is performed on visible views without scrolling.

    See below links for detailed information:

    • UiScrollable, getChildByDescription, getChildByText, getChildByInstance
    • UiCollection, getChildByDescription, getChildByText, getChildByInstance

    Above methods support chained invoking, e.g. for below hierarchy

    <node index="0" text="" resource-id="android:id/list" class="android.widget.ListView" ...>
      <node index="0" text="WIRELESS & NETWORKS" resource-id="" class="android.widget.TextView" .../>
      <node index="1" text="" resource-id="" class="android.widget.LinearLayout" ...>
        <node index="1" text="" resource-id="" class="android.widget.RelativeLayout" ...>
          <node index="0" text="Wi‑Fi" resource-id="android:id/title" class="android.widget.TextView" .../>
        </node>
        <node index="2" text="ON" resource-id="com.android.settings:id/switchWidget" class="android.widget.Switch" .../>
      </node>
      ...
    </node>

    settings

    We want to click the switch at the right side of text 'Wi‑Fi' to turn on/of Wi‑Fi. As there are several switches with almost the same properties, so we can not use like d(className="android.widget.Switch") to select the ui object. Instead, we can use code below to select it.

    d(className="android.widget.ListView", resourceId="android:id/list") \
      .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \
      .child(className="android.widget.Switch") \
      .click()
  • relative position

    Also we can use the relative position methods to get the view: left, right, top, bottom.

    • d(A).left(B), means selecting B on the left side of A.
    • d(A).right(B), means selecting B on the right side of A.
    • d(A).up(B), means selecting B above A.
    • d(A).down(B), means selecting B under A.

    So for above case, we can write code alternatively:

    ## select "switch" on the right side of "Wi‑Fi"
    d(text="Wi‑Fi").right(className="android.widget.Switch").click()
  • Multiple instances

    Sometimes the screen may contain multiple views with the same e.g. text, then you will have to use "instance" properties in selector like below:

    d(text="Add new", instance=0)  # which means the first instance with text "Add new"

    However, uiautomator provides list like methods to use it.

    # get the count of views with text "Add new" on current screen
    d(text="Add new").count
    
    # same as count property
    len(d(text="Add new"))
    
    # get the instance via index
    d(text="Add new")[0]
    d(text="Add new")[1]
    ...
    
    # iterator
    for view in d(text="Add new"):
        view.info  # ...

    Notes: when you are using selector like a list, you must make sure the screen keep unchanged, else you may get ui not found error.

Get the selected ui object status and its information

  • Check if the specific ui object exists

    d(text="Settings").exists # True if exists, else False
    d.exists(text="Settings") # alias of above property.
  • Retrieve the info of the specific ui object

    d(text="Settings").info

    Below is a possible result:

    { u'contentDescription': u'',
      u'checked': False,
      u'scrollable': False,
      u'text': u'Settings',
      u'packageName': u'com.android.launcher',
      u'selected': False,
      u'enabled': True,
      u'bounds': {u'top': 385,
                  u'right': 360,
                  u'bottom': 585,
                  u'left': 200},
      u'className': u'android.widget.TextView',
      u'focused': False,
      u'focusable': True,
      u'clickable': True,
      u'chileCount': 0,
      u'longClickable': True,
      u'visibleBounds': {u'top': 385,
                         u'right': 360,
                         u'bottom': 585,
                         u'left': 200},
      u'checkable': False
    }
    
  • Set/Clear text of editable field

    d(text="Settings").clear_text()  # clear the text
    d(text="Settings").set_text("My text...")  # set the text

Perform the click action on the seleted ui object

  • Perform click on the specific ui object

    # click on the center of the specific ui object
    d(text="Settings").click()
    # click on the bottomright corner of the specific ui object
    d(text="Settings").click.bottomright()
    # click on the topleft corner of the specific ui object
    d(text="Settings").click.topleft()
    # click and wait until the new window update
    d(text="Settings").click.wait()
  • Perform long click on the specific ui object

    # long click on the center of the specific ui object
    d(text="Settings").long_click()
    # long click on the bottomright corner of the specific ui object
    d(text="Settings").long_click.bottomright()
    # long click on the topleft corner of the specific ui object
    d(text="Settings").long_click.topleft()

Gesture action for the specific ui object

  • Drag the ui object to another point or ui object

    # notes : drag can not be set until Android 4.3.
    # drag the ui object to point (x, y)
    d(text="Settings").drag.to(x, y, steps=100)
    # drag the ui object to another ui object(center)
    d(text="Settings").drag.to(text="Clock", steps=50)
  • Swipe from the center of the ui object to its edge

    Swipe supports 4 directions:

    • left
    • right
    • top
    • bottom
    d(text="Settings").swipe.right()
    d(text="Settings").swipe.left(steps=10)
    d(text="Settings").swipe.up(steps=10)
    d(text="Settings").swipe.down()
  • Two point gesture from one point to another

    d(text="Settings").gesture((sx1, sy1), (sx2, sy2)) \
                      .to((ex1, ey1), (ex2, ey2))
  • Two point gesture on the specific ui object

    Supports two gestures:

    • In, from edge to center
    • Out, from center to edge
    # notes : pinch can not be set until Android 4.3.
    # from edge to center. here is "In" not "in"
    d(text="Settings").pinch.In(percent=100, steps=10)
    # from center to edge
    d(text="Settings").pinch.Out()
  • 3 point gesture

    d().gestureM((sx1, sy1), (sx2, sy2),(sx3, sy3)) \
                      .to((ex1, ey1), (ex2, ey2),(ex3,ey3))
    d().gestureM((100,200),(300,200),(600,200),(100,600),(300,600),(600,900))
  • Wait until the specific ui object appears or gone

    # wait until the ui object appears
    d(text="Settings").wait.exists(timeout=3000)
    # wait until the ui object gone
    d(text="Settings").wait.gone(timeout=1000)
  • Perform fling on the specific ui object(scrollable)

    Possible properties:

    • horiz or vert
    • forward or backward or toBeginning or toEnd
    # fling forward(default) vertically(default) 
    d(scrollable=True).fling()
    # fling forward horizentally
    d(scrollable=True).fling.horiz.forward()
    # fling backward vertically
    d(scrollable=True).fling.vert.backward()
    # fling to beginning horizentally
    d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
    # fling to end vertically
    d(scrollable=True).fling.toEnd()
  • Perform scroll on the specific ui object(scrollable)

    Possible properties:

    • horiz or vert
    • forward or backward or toBeginning or toEnd, or to
    # scroll forward(default) vertically(default)
    d(scrollable=True).scroll(steps=10)
    # scroll forward horizentally
    d(scrollable=True).scroll.horiz.forward(steps=100)
    # scroll backward vertically
    d(scrollable=True).scroll.vert.backward()
    # scroll to beginning horizentally
    d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)
    # scroll to end vertically
    d(scrollable=True).scroll.toEnd()
    # scroll forward vertically until specific ui object appears
    d(scrollable=True).scroll.to(text="Security")

Contribution

  • Fork the repo, and clone to your computer.
  • Checkout a new branch from develop branch
  • Install requirements: pip install -r requirements.txt
  • Make your changes, and update tests. Don't forget adding your name at the end of 'Contributors' section
  • Pass all tests and your code must be covered: tox.
  • Commit your changes and submit pull request to develop branch.

Contributors

Issues & Discussion

If you have any bug reports or annoyances please report them to our issue tracker at github issues.

Notes

  • Android uiautomator works on Android 4.1+, so before using it, make sure your device is Android4.1+.
  • Some methods are only working on Android 4.2/4.3, so you'd better read detailed java documentation of uiautomator before using it.
  • The module uses uiautomator-jsonrpc-server as its daemon to communicate with devices.
  • The module is only tested on python2.7/3.2/3.3/pypy.

FAQ

  • Could not start JSONRPC server: raise IOError("RPC server not started!")

    It may be caused by network, device, or environment. So when you meet the issue, please follow below steps and try to manually start the JSONRPC server.

    1. Follow steps at uiautomator-jsonrpc-server to start jsonrpc server.

    2. Check if jsonrpc server is ok:

       curl -d '{"jsonrpc":"2.0","method":"deviceInfo","id":1}' localhost:9008/jsonrpc/0
      

      If you see message like {"jsonrpc":"2.0","id":1,"result":{"currentPackageName":"android","displayHeight":1280,"displayRotation":0,"displaySizeDpX":0,"displaySizeDpY":0,"displayWidth":720,"productName":"falcon","sdkInt":17,"naturalOrientation":true}}, it means the server is up.

    3. check local python lib: python2.7/dist-packages/uiautomator/libs:

      device sdk_level < 18 should use jar. device sdk_level >= 18 use APK.
      

    If you can manually start the jsonrpc server, but your script always meets IOError("RPC server not started!"), please submit an issue at github issues.

  • Error httplib.BadStatusLine: ''

    JsonRPC server needs to access temp directory on device, but on some low tier devices, it may meet error during accessing temp files without SD-CARD attached. So if you met the error, please insert a SD-CARD and then try again.

License

MIT

uiautomator's People

Contributors

eric-valente avatar gitter-badger avatar hongbinbao avatar jjqq2013 avatar jonbeeler avatar libyoung avatar mingyuan-xia avatar xiaocong avatar xiscoxu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uiautomator's Issues

Python wrapper for uiautomator is not working

As per your instruction i had installed uiautomator in my ubuntu12.04 machine using below command.
pip install uiautomator
I had android SDK path in bashrc file also.
export ANDROID_HOME=~/adt-bundle-linux-x86_64-20130917/sdk/

After above steps i had open terminal and opened python and executed below commands
python
Python 2.7.3 (default, Apr 10 2013, 06:20:15)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.

from uiautomator import device as d
d.info
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 381, in info
return self.server.jsonrpc.deviceInfo()
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 280, in jsonrpc
self.start()
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 310, in start
raise IOError("RPC server not started!")
IOError: RPC server not started!
exit()

I am getting RPC server not found can you please suggest any thing need be configure in my Ubuntu machine to use python wrapper for uiautomator.

IOError: RPC server not started!

Hi,

I am getting the RPC server not started error, upon running Uiautomator python scripts. I am getting this error only my ubuntu system. When I connect same device to another ubuntu system, the Uiautomator script runs fine.

Problem seems like the jsonrpc server is not starting. Even tried to start in manually as mentioned in the FAQs, but its doesnt seem to work.

Please find the logs below:
https://gist.github.com/sush35/0e378ac6c6eddbcb199a#file-manual-server-start-logs

Thanks!

Clickable search criteria doesn't work

I am automating an app called 0xBenchmark. It has two UIs with text set to "2D" but one is clickable and the other one is not.
Hi Xiaocong
When I write d(text="2D", clickable=True).click(), it seems it picks the first instance of the UI with text="2D" which in fact is not clickable (I verified this using uiautomatorviewer)

I also tried checkable and focusable and both work correctly and it is able to pick the right UI but it seems clickable search criteria doesn't work.

Best regards

urlib2.URLError

On Ubuntu 11.10, using Python 2.7 and uiautomator 1.16, the following error is encountered:
usrlib2.URLError:<urlopen error [Errno -2] Name or service not known

Insight/assistance on what the issue is and how to resolve would be greatly appreciated.

about watcher

Hi xiaocong, I'm using your wrapper recently. It works well except watcher. When a test case that maybe less than 15 minutes, the watcher works well. But If I run a long-time case, the watcher sometimes doesn't work. Do you know why? Please let me know, thanks!

How to capture adb logcat using adb.raw_cmd?

Tried using

d.server.adb.raw_cmd('-s 12250522501812 shell logcat > 1.log').communicate()

But this command stuck while executing. Any idea

Any one has better solution to capture adb logcat while running test script?

Communicating two devices using uiautomator

Can we communicate two devices with single script sending one command to call to second device and in the second device can we receive the call, with same one script using this framework.

I know using python framework for android we can achieve this by connecting one device as master and another device as slave.

connect device fail

run
d.info

throw exception
File "uiautomator.py", line 264, in jsonrpc
self.start()
File "uiautomator.py", line 281, in start
files = self.download_and_push()
File "uiautomator.py", line 254, in download_and_push
adb.cmd("push", filename, "/data/local/tmp/").wait()
File "uiautomator.py", line 194, in cmd
cmd_line = ["-s", self.device_serial()] + list(args)
File "uiautomator.py", line 205, in device_serial
devices = self.devices()
File "uiautomator.py", line 219, in devices
out = self.raw_cmd("devices").communicate()[0].decode("utf-8")
File "uiautomator.py", line 199, in raw_cmd
cmd_line = [self.adb()] + list(args)
File "uiautomator.py", line 176, in adb
if "ANDROID_HOME" in os.environ:
RuntimeError: maximum recursion depth exceeded while calling a Python object

nanoHttpd update request.

Hi Xiaocong,
Can you please update the android-json-rpc jars with latest nanohttpd code ?
if it is not possible, let us know what are the files to take care to do that.
Reason for this, there are multiple update happened in the nanohttpd project related to connection issues.

Thanks
Sridhar

Atom x86 Image

I'm running a AVD using the latest google Atom x86 image. When trying to run uiautomator scrips i'm running into issues. When using a live device i'm not seeing this, any ideas?

When running:
adb shell uiautomator runtest bundle.jar uiautomator-stub.jar -c com.github.uiautomatorstub.Stub

I get this warning and then the cmd prompt hangs. No commands are working for UIAutomator after this.

WARNING: linker: libdvm.so has text relocations. This is wasting memory and is a security risk. Please fix.
WARNING: linker: libdvm.so has text relocations. This is wasting memory and is a security risk. Please fix.
WARNING: linker: libdvm.so has text relocations. This is wasting memory and is a security risk. Please fix.
INSTRUMENTATION_STATUS: numtests=1
INSTRUMENTATION_STATUS: stream=
com.github.uiautomatorstub.Stub:
INSTRUMENTATION_STATUS: id=UiAutomatorTestRunner
INSTRUMENTATION_STATUS: test=testUIAutomatorStub
INSTRUMENTATION_STATUS: class=com.github.uiautomatorstub.Stub
INSTRUMENTATION_STATUS: current=1
INSTRUMENTATION_STATUS_CODE: 1

Can not select specific UI object

Hi

My app node tree is like blow
image

In pic, there is only one node named "RelativeLayout" and the bounds attribute of this node is [0,75] to [1080,1920].when I got info from this UI object with command (device(className="android.widget.RelativeLayout").info) and its bounds attribute from result is [0,0] to [1080,1920].I think it didnt select right object. Did I miss something?

image

Executing watchers in the same sequence as they registered

watchers registration sequence

d.watcher("alert_checkbox")...
d.watcher("alert_cancel")...
d.watcher("alert_ok")...

watchers calling sequence

d.watcher("alert_ok")...
d.watcher("alert_cancel")...
d.watcher("alert_checkbox")...

Is there any way to execute watchers in same sequence as they are dependents on each other

SyntaxError: invalid syntax at File "uiautomator.py", line 151 kwargs = {k: self[k] for k in self if k not in [self.__mask, self.__childOrSibling, self.__childOrSiblingSelector]}

while installing on python2.6 , got the following error.

pip install uiautomator
Downloading/unpacking uiautomator
Running setup.py egg_info for package uiautomator
Traceback (most recent call last):
File "", line 13, in
File "/home/vgm368/build/uiautomator/setup.py", line 4, in
import uiautomator
File "uiautomator.py", line 151
kwargs = {k: self[k] for k in self if k not in [self.__mask, self.__childOrSibling, self.__childOrSiblingSelector]}
^
SyntaxError: invalid syntax
Complete output from command python setup.py egg_info:
Traceback (most recent call last):

File "", line 13, in

File "/home/vgm368/build/uiautomator/setup.py", line 4, in

import uiautomator

File "uiautomator.py", line 151

kwargs = {k: self[k] for k in self if k not in [self.__mask, self.__childOrSibling, self.__childOrSiblingSelector]}

                       ^

SyntaxError: invalid syntax


Command python setup.py egg_info failed with error code 1
Storing complete log in ./pip-log.txt

UiAutomationService down sometimes.

d.dump('screen.xml')
Traceback (most recent call last):
File "", line 1, in
File "C:\Python27\lib\site-packages\uiautomator.py", line 401, in dump
device_file = self.server.jsonrpc.dumpWindowHierarchy(True, "dump.xml")
File "C:\Python27\lib\site-packages\uiautomator.py", line 79, in call
(jsonresult["error"]["code"], jsonresult["error"]["message"]))
Exception: Error response. Error code: 0, Error message: UiAutomationService not
connected. Did you call #register()?

Visible Text

Hi @xiaocong

I am a heavy user of this and here is one challenge I have discovered,

There are certain objects on Android that have text in them but the text is not on the "text" and "contentDescription" field (see attachment). I suspect there might be more object that behaves like this one. However, there maybe a possible solution to this. I don't know complex it maybe from your side to create a function like "getvisible text".

Here is how it can work:

  • Take a snapshot of the specified object
  • Scan through it using "ocr" like methods
  • Print out text from 'ocr"

I know "ocr" is far from perfect but at least it will retrieve some text.

Device Used: Galaxy S5 4.4.2

uiautomator_screenshot

className is shown as None even though it is "android.widget.TextView" in dump.xml

uiautomator version: "0.1.26"
Android OS version: 4.1.1

d(text='Manual').info
{u'contentDescription': u'', u'checked': False, u'scrollable': False, u'text': u'Manual', u'packageName': u'com.android.settings', u'selected': False, u'enabled': True, u'bounds': {u'top': 249, u'left': 778, u'right': 1404, u'bottom': 282}, u'className': None, u'focusable': False, u'focused': False, u'clickable': False, u'checkable': False, u'chileCount': 0, u'longClickable': False, u'visibleBounds': None}

d.server.adb.cmd('push idle.xml /data/tmp/inbox').communicate() throws error

was just trying to push a file using d.server.adb.cmd on Windows , i get into the following error , what is the command does python execute so that it shows adb help as error.

cmd_line:['D:\Work\adt-bundle-windows\sdk\platform-tools\adb.exe', 'devices']
cmd_line:['D:\Work\adt-bundle-windows\sdk\platform-tools\adb.exe', '-s', u'6B1C00001400B01B', 'push idle.xml /data/tmp/inbox']
('', 'Android Debug Bridge version 1.0.31\r\n\r\n -a - directs adb to listen on all interfaces for a connection\r\n -d - directs command to the on
ly connected USB device\r\n returns an error if more than one USB device is present.\r\n -e - directs command to the only running emulator.\r
\n returns an error if more than one emulator is running.\r\n -s - directs command to the device or emulator with the given\r\n

etc...

BadStatusLine exception

ERROR: This test case is to record audio and playback in sound recorder app.

Traceback (most recent call last):
File "/home/wanshuang/code_review_xiuhua/otc_qa_tools-ata/tests/samples/soundrecord.py", line 47, in tearDown
g_common_obj.stop_exp_handle()
File "/home/wanshuang/code_review_xiuhua/otc_qa_tools-ata/tests/utils/common.py", line 84, in stop_exp_handle
self.get_device().watchers.remove()
File "/usr/local/lib/python2.7/dist-packages/uiautomator-0.1.27-py2.7.egg/uiautomator.py", line 574, in remove
obj.server.jsonrpc.removeWatcher(name)
File "/usr/local/lib/python2.7/dist-packages/uiautomator-0.1.27-py2.7.egg/uiautomator.py", line 362, in wrapper
return _method_obj(_args, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/uiautomator-0.1.27-py2.7.egg/uiautomator.py", line 98, in call
result = urllib2.urlopen(req, timeout=self.timeout)
File "/usr/lib/python2.7/urllib2.py", line 126, in urlopen
return _opener.open(url, data, timeout)
File "/usr/lib/python2.7/urllib2.py", line 400, in open
response = self._open(req, data)
File "/usr/lib/python2.7/urllib2.py", line 418, in _open
'_open', req)
File "/usr/lib/python2.7/urllib2.py", line 378, in _call_chain
result = func(*args)
File "/usr/lib/python2.7/urllib2.py", line 1207, in http_open
return self.do_open(httplib.HTTPConnection, req)
File "/usr/lib/python2.7/urllib2.py", line 1180, in do_open
r = h.getresponse(buffering=True)
File "/usr/lib/python2.7/httplib.py", line 1030, in getresponse
response.begin()
File "/usr/lib/python2.7/httplib.py", line 407, in begin
version, status, reason = self._read_status()
File "/usr/lib/python2.7/httplib.py", line 371, in _read_status
raise BadStatusLine(line)
BadStatusLine: ''

1 second delay for tcp connection in windows

It takes 1 second to establish tcp connection in windows.

Request Count: 1
Bytes Sent: 257 (headers:179; body:78)
Bytes Received: 204 (headers:131; body:73)

ACTUAL PERFORMANCE

ClientConnected: 17:31:57.413
ClientBeginRequest: 17:31:57.418
GotRequestHeaders: 17:31:57.418
ClientDoneRequest: 17:31:57.418
Determine Gateway: 0ms
DNS Lookup: 1ms
TCP/IP Connect: 1001ms
HTTPS Handshake: 0ms
ServerConnected: 17:31:58.422
FiddlerBeginRequest: 17:31:58.422
ServerGotRequest: 17:31:58.422
ServerBeginResponse: 17:31:58.443
GotResponseHeaders: 17:31:58.443
ServerDoneResponse: 17:31:58.446
ClientBeginResponse: 17:31:58.446
ClientDoneResponse: 17:31:58.446

Overall Elapsed: 0:00:01.028

Start RFC server failed

Hi

I had some issues to start RPC server on win7. image

And I refered issue #7 did some steps blow.

  1. dir temp/libs
    2013/12/20 03:36 .
    2013/12/20 03:36 ..
    2013/12/20 03:36 461,489 bundle.jar
    2013/12/20 03:36 32,685 uiautomator-stub.jar

2.adb shell getprop ro.build.version.release
4.1.2

3.adb shell ps -C uiautomator
USER PID PPID VSIZE RSS WCHAN PC NAME
shell 10669 10667 651052 30640 ffffffff 401c0ce4 S uiautomator

4.adb logcat -d after error occurred and mailed to you.

IOError: RPC server not started!

Hi:
I had problems to start the RPC server.

from uiautomator import device as d

d.info
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 412, in info
return self.server.jsonrpc.deviceInfo()
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 319, in jsonrpc
self.start()
File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 337, in start
raise IOError("RPC server not started!")
IOError: RPC server not started!

Did i forgot some steps?

CR--How to support more android api such as Call...

Dear xiaocong

I need some interface to call android api to test android system ,it will save a lot of
scripting work, for example , if I want to test phone call function , I have to write script
step by step to dial a MO call(press home=>go to dial pad=>input dial number=>make call). but if there is a api like "bool CallPhone(string phoneNo)" ,
then I only need to call this api directly. and I will work only all android phone .

Do you have any idea to develope this , I need some api like:
bool CallPhone(string phoneNo)
bool CallPhone(string phoneNo)
int GetCallState()
bool OpenUrl(string url)
bool SMS_Send (string phoneNo, string content)
bool MMS_Send (string phoneNo, string subject, string body,string type,string attachmentPath)

can't do long_click on a text field?

if d(text="Calendar").click() works why can't d(text="Calendar").long_click() work.

d(text="Calendar").long_click() throws an error

Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python2.7/dist-packages/uiautomator/init.py", line 71, in call
return self.func(_args, *_kwargs)
File "/usr/local/lib/python2.7/dist-packages/uiautomator/init.py", line 844, in _long_click
x = (bounds["left"] + bounds["right"])/2
TypeError: 'NoneType' object has no attribute 'getitem'

It can be supported right? particularly useful to get context sensitive menu popped up.

volume_mute is not working as expected.

Hi,
I am running this on my Nexus 7. Volume_mute is not working. Volume_up and down are working as expected.
Volume_mute command output is true but it is not reflecting on device.

adb not found on window OS

on window OS:
adb not found even though set the ANDROID_HOME
the ADB file name should be:
$ANDROID_HOME/platform-tools/adb.exe

Starting Server

Hi,

I am newbie on Android so apologies for my question that may be dumb. I am trying to run the jsonrcp server on Android device using "adb shell uiautomator runtest uiautomator-stub.jar bundle.jar -c com.github.uiautomatorstub.Stub" but I am getting the following:-

INSTRUMENTATION_RESULT: longMsg=Didn't find class "com.github.uiautomatorstub.Stub" on path: /system/framework/android.test.runner.jar:/system/framework/uiautomator.jar::/data/local/tmp/uiautomator-stub.jar:/data/local/tmp/bundle.jar
INSTRUMENTATION_CODE: 0

I have tried to follow all the steps on the instruction. I am not sure what I have missed. Please help, how can I solve this?

nested parent and child selector

d(**A).from_parent(**B) means from A's parent find its child B

d(**A).child_selector(**B) means from A find its child B

But we can not use like d(**A).from_parent(**B).child_selector(**C), which means from A's parent find its child B, and then find'sB's child C.

It's a design issue.

Question about forward_list

In class Adb, the function forward_list will return the correct forward port list on OS 'posix'.
My working environment is on Windows 7 64 bit.
The function will only return empty list and thus the AutomatorServer will establish a new adb forward port disregarding the already existed ones.
I added 'nt' in the if condition and it returns the correct list.
I wonder why restricting the function to OS 'posix', is there any other concern I don't know?
Thanks.

ImportError: No module named urllib3 when install in WIN7 64bit PC and Python2.7.6

with WIN7 64bit PC and Python2.7.6 installed
when download the source and install by running setup.py install.
show this error:
E:\MTBF\外部工具\GoogleProject\uiautomator\uiautomator-master-xiaocong\uiautomat
or-master>E:\MTBF\外部工具\GoogleProject\uiautomator\uiautomator-master-xiaocong
\uiautomator-master\setup.py install
Traceback (most recent call last):
File "E:\MTBF\外部工具\GoogleProject\uiautomator\uiautomator-master-xiaocong\u
iautomator-master\setup.py", line 4, in
import uiautomator
File "E:\MTBF\外部工具\GoogleProject\uiautomator\uiautomator-master-xiaocong\u
iautomator-master\uiautomator.py", line 25, in
import urllib3
ImportError: No module named urllib3

Once start jsonrpc, user encounter "android.accessibilityservice.IAccessibilityServiceClient has already registered" issue

hi, dear xiaocong
when running execution through jsonrpc, user is unable to use ui-automator viewer to design next case step or run adb shell uiautomator runtest from terminal, shall we support another kind of mode --- case design mode, it will permit user to use ui-automator command.
Error logs:


INSTRUMENTATION_RESULT: shortMsg=java.lang.IllegalStateException
INSTRUMENTATION_RESULT: longMsg=UiAutomationService android.accessibilityservice.IAccessibilityServiceClient$Stub$Proxy@6a64d4a8already registered!
INSTRUMENTATION_CODE: 0


Question about forward_list

In class Adb, function forward_list returns the correct tcp forward port number list only on OS 'posix'.
My working environment is on Windows 7 64 bit.
The function will only return a empty list, thus I will establish another adb forward port to device disregarding the already existed port forwarding.
I wonder why restricting the function to OS 'posix', is there other concern I don't know?
Thanks.

can't start uiautomator - httplib.InvalidURL: nonnumeric port: 'aport'

Hi, I have following error on 'd.info()'

echo 'from uiautomator import device as d;print(d.info)' | python

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 476, in info
    return self.server.jsonrpc.deviceInfo()
  File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 369, in wrapper
    server.start()
  File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 385, in start
    files = self.download_and_push()
  File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 342, in download_and_push
    self.download(filename, url)
  File "/usr/local/lib/python2.7/dist-packages/uiautomator.py", line 350, in download
    res = urllib2.urlopen(url)
  File "/usr/lib/python2.7/urllib2.py", line 127, in urlopen
    return _opener.open(url, data, timeout)
  File "/usr/lib/python2.7/urllib2.py", line 404, in open
    response = self._open(req, data)
  File "/usr/lib/python2.7/urllib2.py", line 422, in _open
    '_open', req)
  File "/usr/lib/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 1222, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib/python2.7/urllib2.py", line 1153, in do_open
    h = http_class(host, timeout=req.timeout) # will parse host:port
  File "/usr/lib/python2.7/httplib.py", line 1164, in __init__
    source_address)
  File "/usr/lib/python2.7/httplib.py", line 704, in __init__
    self._set_hostport(host, port)
  File "/usr/lib/python2.7/httplib.py", line 732, in _set_hostport
    raise InvalidURL("nonnumeric port: '%s'" % host[i+1:])
httplib.InvalidURL: nonnumeric port: 'aport'

how can I fix this?

d.click(805,805) crashes android.

d.click(805,805) command crashes android.

But the same command getDevice().click(805,805) works.

ro.secure=1
ro.debuggable=1

D/UIAutomatorStub( 1668): URI: /jsonrpc/0, Method: POST, Header: {content-type=application/json, user-agent=Python-urllib/2.6, connection=close, host=localhost:9008, accept-encoding=identity, content-length=78}, parms, {{"jsonrpc": "2.0", "method": "ping", "id": "2aad0a5903032a49fefe812a06e658e2"}=, NanoHttpd.QUERY_STRING={"jsonrpc": "2.0", "method": "ping", "id": "2aad0a5903032a49fefe812a06e658e2"}}, files: {}
W/ResourceType( 845): No known package when getting name for resource number 0x9b060000
W/ResourceType( 845): No known package when getting name for resource number 0x9b050001
W/ResourceType( 845): No known package when getting name for resource number 0x9b040002
W/ResourceType( 845): No known package when getting name for resource number 0x9b020004
W/ResourceType( 845): No known package when getting name for resource number 0x9b010005
W/ResourceType( 845): No known package when getting name for resource number 0x9b000006
W/ResourceType( 845): No known package when getting name for resource number 0x9c030000
W/ResourceType( 845): No known package when getting name for resource number 0x9c020000
I/QueryController( 1668): Matched selector: UiSelector[] <<==>> [android.view.accessibility.AccessibilityNodeInfo@7442; boundsInParent: Rect(0, 0 - 1920, 1104); boundsInScreen: Rect(0, 0 - 1920, 1104); packageName: com.android.launcher; className: android.widget.FrameLayout; text: null; contentDescription: null; viewIdResName: null; checkable: false; checked: false; focusable: false; focused: false; selected: false; clickable: false; longClickable: false; enabled: true; password: false; scrollable: false; [ACTION_SELECT, ACTION_CLEAR_SELECTION, ACTION_ACCESSIBILITY_FOCUS]]
D/UIAutomatorStub( 1668): URI: /jsonrpc/0, Method: POST, Header: {content-type=application/json, user-agent=Python-urllib/2.6, connection=close, host=localhost:9008, accept-encoding=identity, content-length=101}, parms, {{"params": [805, 805], "jsonrpc": "2.0", "method": "click", "id": "5ce39999f62f14da963de8e7156b114b"}=, NanoHttpd.QUERY_STRING={"params": [805, 805], "jsonrpc": "2.0", "method": "click", "id": "5ce39999f62f14da963de8e7156b114b"}}, files: {}
D/InteractionController( 1668): clickNoSync (805, 805)
E/InputEventReceiver( 542): Exception dispatching input event.
E/MessageQueue-JNI( 542): Exception in MessageQueue callback: handleReceiveCallback
E/MessageQueue-JNI( 542): java.lang.NullPointerException
E/MessageQueue-JNI( 542): at com.android.internal.widget.PointerLocationView.addPointerEvent(PointerLocationView.java:552)
E/MessageQueue-JNI( 542): at com.android.internal.policy.impl.PhoneWindowManager$PointerLocationInputEventReceiver.onInputEvent(PhoneWindowManager.java:310)
E/MessageQueue-JNI( 542): at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:182)
E/MessageQueue-JNI( 542): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MessageQueue-JNI( 542): at android.os.MessageQueue.next(MessageQueue.java:132)
E/MessageQueue-JNI( 542): at android.os.Looper.loop(Looper.java:124)
E/MessageQueue-JNI( 542): at android.os.HandlerThread.run(HandlerThread.java:61)
W/dalvikvm( 542): threadid=12: thread exiting with uncaught exception (group=0x41592700)
E/AndroidRuntime( 542): *** FATAL EXCEPTION IN SYSTEM PROCESS: UI
E/AndroidRuntime( 542): java.lang.NullPointerException
E/AndroidRuntime( 542): at com.android.internal.widget.PointerLocationView.addPointerEvent(PointerLocationView.java:552)
E/AndroidRuntime( 542): at com.android.internal.policy.impl.PhoneWindowManager$PointerLocationInputEventReceiver.onInputEvent(PhoneWindowManager.java:310)
E/AndroidRuntime( 542): at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:182)
E/AndroidRuntime( 542): at android.os.MessageQueue.nativePollOnce(Native Method)
E/AndroidRuntime( 542): at android.os.MessageQueue.next(MessageQueue.java:132)
E/AndroidRuntime( 542): at android.os.Looper.loop(Looper.java:124)
E/AndroidRuntime( 542): at android.os.HandlerThread.run(HandlerThread.java:61)
I/Process ( 542): Sending signal. PID: 542 SIG: 9

android.webkit.WebView

Hi @xiaocong

We have an android.webkit.WebView that has an html passed through to it. On it's properties, "text" and "content-desc" are bank but the correct text has been displayed on the screen. How can we retrieve the html passed to it or retrieve the visible text?

When I do a object.info on this object, this it what I get:-
"{'bounds': {'right': 465, 'bottom': 260, 'top': 194, 'left': 15}, 'className': None, 'longClickable': True, 'selected': False, 'clickable': True, 'contentDescription': '', 'checkable': False, 'checked': False, 'chileCount': 0, 'text': '', 'packageName': 'za.co.fnb.connect.itt', 'visibleBounds': {'right': 465, 'bottom': 260, 'top': 194, 'left': 15}, 'scrollable': False, 'focusable': True, 'focused': False, 'enabled': True}"

Here is the IU Automator Viewer screenshot

ui automator viewer

PY UIAutomator: Need Addition of Screenshot Watcher

Hey there,

What is the purpose of bundle.jar and uiautomator-stub.jar?
If I want to change Server code, how to compile these two jars myself?
P.S.
I want to add Watcher to take screenshot by any testcase failure. I will add in the jsonserver code and then compile it to jar and use it uiautomator.py

Python 2.4 Issues

This is mainly an FYI as I don't know if this is fixable or if the requirements should be officially non-2.4 and below. When trying to install uiautomator with Python 2.14 this is received:

C:\Users\Mbadmin\Downloads\uiautomator-0.1.27>c:\Python24\python.exe setup.py
Traceback (most recent call last):
  File "setup.py", line 4, in ?
    import uiautomator
  File "C:\Users\Mbadmin\Downloads\uiautomator-0.1.27\uiautomator.py", line 200
    top = rect1["top"] if rect1["top"] > rect2["top"] else rect2["top"]
                        ^
SyntaxError: invalid syntax

Hope this helps!

Device reboot requires explicit sleep even after "adb wait-for-device"

Code:

self.d.server.adb.raw_cmd("root").communicate()
subprocess.Popen('adb nodaemon wait-for-device'.split(), stdout=subprocess.PIPE).communicate()
self.d.server.adb.raw_cmd("reboot").communicate()
subprocess.Popen('adb nodaemon wait-for-device'.split(), stdout=subprocess.PIPE).communicate()
#time.sleep(10)
self.d(packageName="android").wait.exists(timeout=9000)

Error:

Traceback (most recent call last):
File "test_reboot.py", line 34, in testMultiReboot
self.reboot()
File "test_reboot.py", line 28, in reboot
self.d(packageName="android").wait.exists(timeout=9000)
File "/home/git/work/uiautomator/uiautomator.py", line 397, in call
return AutomatorDeviceObject(self, Selector(**kwargs))
File "/home/git/work/uiautomator/uiautomator.py", line 809, in init
super(AutomatorDeviceObject, self).init(device, selector)
File "/home/git/work/uiautomator/uiautomator.py", line 637, in init
self.jsonrpc = device.server.jsonrpc
File "/home/git/work/uiautomator/uiautomator.py", line 319, in jsonrpc
self.start()
File "/home/git/work/uiautomator/uiautomator.py", line 337, in start
raise IOError("RPC server not started!")
IOError: RPC server not started!

But after un-commenting time.sleep(), i do not see this issue, Does RPC server start after few seconds after reboot?

Add a callback interface for Watcher

Currently, Watcher can trigger defined simple action, e.g, click when condition match. However, sometimes, we expect do more complex action. It is better provide a callback interface for watcher, thus user can define whatever customized action in a function.

截屏纵向压缩

在测试android机顶盒时,使用d.screenshot("C:\Users\Administrator\Desktop\1.png"),
打开图片后发现截图纵向压缩了,如图

1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.