Compare commits

...
36 Commits
Author SHA1 Message Date
butomo1989 0e578710f3 Automated all steps from PR #12 2017-06-14 15:06:01 +02:00
butomo1989 65df456066 Using docker-appium as base image 2017-06-14 15:04:19 +02:00
butomo1989 5b694c2c55 Fixed issue #13 regarding arm image for 6.0 2017-06-09 11:59:07 +02:00
AndrejsandBudi Utomo 6406504f94 Possibility to run appium with custom chromedriver executable (#12)
* Possibility to run appium with custom chromedriver executable
* Unit test added
* README update
2017-06-09 10:44:58 +02:00
butomo1989 b7c4538896 Downgraded selenium to be able to run E2E Tests 2017-06-02 16:55:51 +02:00
butomo1989 c7d988bd61 Refactored 2017-06-02 15:30:51 +02:00
butomo1989 47e968927a Updated Sample and End2End Tests 2017-06-02 14:49:55 +02:00
butomo1989 dcf196ec4a Run E2E Tests only if needed 2017-06-02 13:18:00 +02:00
butomo1989 aff1b062e1 Added End-2-End Test 2017-06-02 12:14:14 +02:00
butomo1989 59f6e4bd33 Mocked os package in unit tests to clear all warning messages 2017-05-30 17:25:07 +02:00
butomo1989 0b9af7e6b2 Using git tag for same docker images 2017-05-30 16:28:28 +02:00
butomo1989 d9b5f8d86c Using latest version of build tool 2017-05-30 16:26:09 +02:00
butomo1989 0360c181c0 Cleaned up 2017-05-29 14:34:35 +02:00
budi utomoandGitHub 47ff7bf223 Merge pull request #10 from ilya-netomedia/patch-1
Increase unregister delay timeout
2017-05-26 10:28:07 +02:00
ilya-netomediaandGitHub bedf77e83d Update app.py
The Problem : Selenium grid 3.0.1 marking the node as down
Solution: Node config should be changed
2017-05-25 17:48:59 +03:00
budi utomoandGitHub 151e3a8d6f Merge pull request #8 from andrcuns/appium_host_for_scale_support
Support docker-compose scale when connecting to selenium hub
2017-05-24 23:36:39 +02:00
Andrejs Cunskis 7170ee20d4 Fix method name and build 6.0 without google_apis 2017-05-24 17:00:29 +03:00
Andrejs Cunskis 768d793ba0 Merge remote-tracking branch 'upstream/master' into appium_host_for_scale_support 2017-05-24 16:51:59 +03:00
Andrejs Cunskis 5501a20426 Set default browser, mobile web test parameter and docker compose example 2017-05-24 15:50:34 +03:00
butomo1989 1a7453d6b6 Added codacy badge 2017-05-24 12:01:10 +02:00
butomo1989 d379133801 Renamed method to fix codacy violation 2017-05-24 11:52:48 +02:00
Andrejs Cunskis 333c2b2a90 Fix unit tests 2017-05-23 16:57:53 +03:00
Andrejs Cunskis ab45f6bb52 Support for docker-compose scale and set default browserName 2017-05-23 16:47:41 +03:00
Andrejs Cunskis a2e59992d9 Set container ip as default APPIUM_HOST address 2017-05-23 10:12:45 +03:00
butomo1989 e9ed10d08c Splitted into different Dockerfiles to optimize size for each docker image 2017-05-22 13:20:30 +02:00
budi utomoandGitHub d1a1bb5fa7 Deleted parentheses in README.md 2017-05-19 13:42:15 +02:00
butomo1989 c3c7f9506f Using UIAutomator2 for UI test example for better performance 2017-05-19 13:37:25 +02:00
butomo1989 8c4d947833 Updated README 2017-05-19 11:17:19 +02:00
butomo1989 80bbf1bd36 Using latest version of build-tool 2017-05-19 11:16:59 +02:00
butomo1989 f05d2564fa Added more timeout in health check to support arm docker image 2017-05-19 11:16:15 +02:00
budi utomoandGitHub 81e63fb88e Merge pull request #5 from andrcuns/container_health_check
Container health check based on Android Emulator boot completion
2017-05-15 16:15:23 +02:00
Andrejs Cunskis a7c9be6fc3 Container health check based on Android Emulator boot completion 2017-05-15 14:31:26 +03:00
budi utomoandGitHub 76393754fb Updated README.md 2017-05-04 16:32:01 +02:00
budi utomoandGitHub 10eb1b0d31 Merge pull request #3 from andrcuns/chrome_driver_fix
Fix chromedriver linux missing shared library libgconf-2.so.4 error
2017-05-04 10:23:41 +02:00
Andrejs Cunskis c3ad9b3d48 Fix chromedriver linux missing shared library libgconf-2.so.4 error 2017-05-04 01:29:41 +03:00
butomo1989 e2a0b7c840 Put only working devices 2017-04-26 16:34:27 +02:00
24 changed files with 511 additions and 155 deletions
+35 -23
View File
@@ -3,8 +3,9 @@ Docker-Android
[![Build Status](https://travis-ci.org/butomo1989/docker-android.svg?branch=master)](https://travis-ci.org/butomo1989/docker-android) [![Build Status](https://travis-ci.org/butomo1989/docker-android.svg?branch=master)](https://travis-ci.org/butomo1989/docker-android)
[![codecov](https://codecov.io/gh/butomo1989/docker-android/branch/master/graph/badge.svg)](https://codecov.io/gh/butomo1989/docker-android) [![codecov](https://codecov.io/gh/butomo1989/docker-android/branch/master/graph/badge.svg)](https://codecov.io/gh/butomo1989/docker-android)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3f000ffb97db45a59161814e1434c429)](https://www.codacy.com/app/butomo1989/docker-appium?utm_source=github.com&utm_medium=referral&utm_content=butomo1989/docker-appium&utm_campaign=Badge_Grade)
Docker-Android is an android environment, an android emulator that facilitates different devices and [appium] in docker solution integrated with noVNC. Docker-Android is a docker image built to be used for everything related to mobile website testing and Android project.
Samsung Device | Google Device Samsung Device | Google Device
:---------------------------:|:---------------------------: :---------------------------:|:---------------------------:
@@ -13,20 +14,20 @@ Samsung Device | Google Device
Purpose Purpose
------- -------
1. Build android project and run unit test inside container 1. Run UI tests for mobile websites with [appium]
2. Run UI Test for mobile application with different frameworks (appium, espresso, etc.) 2. Build Android project and run unit tests with the latest build-tools
3. Run UI Test for mobile website with appium test framework 3. Run UI tests for Android applications with different frameworks ([appium], [espresso], [robotium], etc.)
4. Video recording to analyse failing test cases ***(soon)*** 4. Run monkey / stress tests
5. Monkey test for stress test ***(soon)*** 5. SMS testing
Advantages compare with other docker-android projects Advantages compare with other docker-android projects
----------------------------------------------------- -----------------------------------------------------
1. noVNC to see what happen inside docker container 1. noVNC to see what happen inside docker container
2. Android emulator that facilitates different devices 2. Emulator for different devices / skins, such as Samsung Galaxy S6, LG Nexus 4, HTC Nexus One and more.
3. Able to connect with selenium grid 3. Ability to connect to Selenium Grid
4. Able to control emulator from outside container by using adb connect 4. Ability to control emulator from outside container by using adb connect
5. It will have more important features for testing purpose like video recording and monkey test ***(soon)*** 5. Open source with more features coming (monkey test, support real devices with screen mirroring and video recording)
List of Docker images List of Docker images
--------------------- ---------------------
@@ -52,15 +53,9 @@ Type | Device Name
Phone | Samsung Galaxy S6 Phone | Samsung Galaxy S6
Phone | Nexus 4 Phone | Nexus 4
Phone | Nexus 5 Phone | Nexus 5
Phone | Nexus 5x
Phone | Nexus 6
Phone | Nexus 6P
Phone | Nexus One Phone | Nexus One
Phone | Nexus S Phone | Nexus S
Tablet | Pixel C
Tablet | Nexus 7 Tablet | Nexus 7
Tablet | Nexus 9
Tablet | Nexus 10
Requirements Requirements
------------ ------------
@@ -107,9 +102,6 @@ docker run --privileged -d -p 6080:6080 -p 5554:5554 -p 5555:5555 -p 4723:4723 -
### Connect to Selenium Grid ### Connect to Selenium Grid
![][selenium grid]
![][devices are connected with selenium grid]
It is also possible to connect appium server that run inside docker-android with selenium grid by passing following environment variables: It is also possible to connect appium server that run inside docker-android with selenium grid by passing following environment variables:
- CONNECT\_TO\_GRID=True - CONNECT\_TO\_GRID=True
@@ -118,8 +110,12 @@ It is also possible to connect appium server that run inside docker-android with
- SELENIUM_HOST="\<host\_ip\_address>" - SELENIUM_HOST="\<host\_ip\_address>"
- SELENIUM_PORT=\<port\_number> - SELENIUM_PORT=\<port\_number>
To run tests for mobile browser, following parameter can be passed:
- MOBILE\_WEB\_TEST=True
```bash ```bash
docker run --privileged -d --rm -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -e DEVICE="Samsung Galaxy S6" -e APPIUM=True -e CONNECT_TO_GRID=True -e APPIUM_HOST="127.0.0.1" -e APPIUM_PORT=4723 -e SELENIUM_HOST="172.17.0.1" -e SELENIUM_PORT=4444 --name android-container butomo1989/docker-android-x86-7.1.1 docker run --privileged -d -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -e DEVICE="Samsung Galaxy S6" -e APPIUM=True -e CONNECT_TO_GRID=True -e APPIUM_HOST="127.0.0.1" -e APPIUM_PORT=4723 -e SELENIUM_HOST="172.17.0.1" -e SELENIUM_PORT=4444 -e MOBILE_WEB_TEST=True --name android-container butomo1989/docker-android-x86-7.1.1
``` ```
### Share Volume ### Share Volume
@@ -127,7 +123,18 @@ docker run --privileged -d --rm -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5
If you want to use appium to test UI of your android application, you need to share volume where the APK is located to folder ***/root/tmp***. If you want to use appium to test UI of your android application, you need to share volume where the APK is located to folder ***/root/tmp***.
```bash ```bash
docker run --privileged -it --rm -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -v $PWD/example/sample_apk:/root/tmp -e DEVICE="Nexus 5" -e APPIUM=True -e CONNECT_TO_GRID=True -e APPIUM_HOST="127.0.0.1" -e APPIUM_PORT=4723 -e SELENIUM_HOST="172.17.0.1" -e SELENIUM_PORT=4444 --name android-container butomo1989/docker-android-x86-7.1.1 docker run --privileged -d -p 6080:6080 -p 4723:4723 -p 5554:5554 -p 5555:5555 -v $PWD/example/sample_apk:/root/tmp -e DEVICE="Nexus 5" -e APPIUM=True -e CONNECT_TO_GRID=True -e APPIUM_HOST="127.0.0.1" -e APPIUM_PORT=4723 -e SELENIUM_HOST="172.17.0.1" -e SELENIUM_PORT=4444 --name android-container butomo1989/docker-android-x86-7.1.1
```
### Docker-Compose
![][compose]
![][connected_devices]
There is [example of compose file] to run complete selenium grid and docker-android container as nodes. [docker-compose] version [1.13.0] or higher is required to be able to execute that compose file.
```bash
docker-compose up -d
``` ```
Control android emulator outside container Control android emulator outside container
@@ -188,9 +195,14 @@ docker exec -it android-container tail -f /var/log/supervisor/docker-android.std
``` ```
[appium]: <https://appium.io> [appium]: <https://appium.io>
[espresso]: <https://google.github.io/android-testing-support-library/docs/espresso/>
[robotium]: <https://github.com/RobotiumTech/robotium>
[docker android samsung]: <images/docker_android_samsung.png> [docker android samsung]: <images/docker_android_samsung.png>
[docker android nexus]: <images/docker_android_nexus.png> [docker android nexus]: <images/docker_android_nexus.png>
[selenium grid]: <images/selenium_grid.png> [compose]: <images/compose.png>
[devices are connected with selenium grid]: <images/connected_with_grid.png> [connected_devices]: <images/connected_devices.png>
[example of compose file]: <docker-compose.yml>
[docker-compose]: <https://docs.docker.com/compose/install/>
[1.13.0]: <https://github.com/docker/compose/releases/tag/1.13.0>
[adb_connection]: <images/adb_connection.png> [adb_connection]: <images/adb_connection.png>
[sms]: <images/SMS.png> [sms]: <images/SMS.png>
+60
View File
@@ -0,0 +1,60 @@
# Note: It requires docker-compose 1.13.0
#
# Usage: docker-compose up -d
version: "2.2"
services:
# Selenium hub
selenium_hub:
image: selenium/hub:3.4.0
ports:
- "4444:4444"
# Docker-Android for Android application testing
nexus_7.1.1:
image: butomo1989/docker-android-x86-7.1.1
privileged: true
# Change path of apk that you want to test. I use sample_apk that I provide in folder "example"
volumes:
- $PWD/example/sample_apk:/root/tmp
# Increase scale number if needed
scale: 1
ports:
- 6080
environment:
- DEVICE=Nexus 5
- CONNECT_TO_GRID=True
- APPIUM=true
- SELENIUM_HOST=selenium_hub
# Docker-Android for mobile website testing with chrome browser
# Chrome browser exists only for version 7.0 and 7.1.1
samsung_galaxy_web_7.1.1:
image: butomo1989/docker-android-x86-7.1.1
privileged: true
# Increase scale number if needed
scale: 1
ports:
- 6080
environment:
- DEVICE=Samsung Galaxy S6
- CONNECT_TO_GRID=True
- APPIUM=true
- SELENIUM_HOST=selenium_hub
- MOBILE_WEB_TEST=True
# Docker-Android for mobile website testing with default browser
# Default browser exists only for version 5.0.1, 5.1.1 and 6.0
samsung_galaxy_web_5.1.1:
image: butomo1989/docker-android-x86-5.1.1
privileged: true
# Increase scale number if needed
scale: 1
ports:
- 6080
environment:
- DEVICE=Samsung Galaxy S6
- CONNECT_TO_GRID=True
- APPIUM=true
- SELENIUM_HOST=selenium_hub
- MOBILE_WEB_TEST=True
+134
View File
@@ -0,0 +1,134 @@
FROM appium/appium:1.6.6-beta-p0
LABEL maintainer "Budi Utomo <budi.ut.1989@gmail.com>"
#=============
# Set WORKDIR
#=============
WORKDIR /root
#==================
# General Packages
#------------------
# xterm
# Terminal emulator
# supervisor
# Process manager
# socat
# Port forwarder
#------------------
# NoVNC Packages
#------------------
# x11vnc
# VNC server for X display
# openbox
# Windows manager
# menu
# Debian menu
# python-numpy
# Numpy, For faster performance: https://github.com/novnc/websockify/issues/77
# net-tools
# Netstat
#==================
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
xterm \
supervisor \
socat \
x11vnc \
openbox \
menu \
python-numpy \
net-tools \
&& rm -rf /var/lib/apt/lists/*
#=======
# noVNC
# Use same commit id that docker-selenium uses
# https://github.com/elgalu/docker-selenium/blob/236b861177bd2917d864e52291114b1f5e4540d7/Dockerfile#L412-L413
#=======
ENV NOVNC_SHA="b403cb92fb8de82d04f305b4f14fa978003890d7" \
WEBSOCKIFY_SHA="558a6439f14b0d85a31145541745e25c255d576b"
RUN wget -nv -O noVNC.zip "https://github.com/kanaka/noVNC/archive/${NOVNC_SHA}.zip" \
&& unzip -x noVNC.zip \
&& rm noVNC.zip \
&& mv noVNC-${NOVNC_SHA} noVNC \
&& wget -nv -O websockify.zip "https://github.com/kanaka/websockify/archive/${WEBSOCKIFY_SHA}.zip" \
&& unzip -x websockify.zip \
&& mv websockify-${WEBSOCKIFY_SHA} ./noVNC/utils/websockify \
&& rm websockify.zip \
&& ln noVNC/vnc_auto.html noVNC/index.html
#======================
# Install SDK packages
#======================
ARG ANDROID_VERSION=5.0.1
ARG API_LEVEL=21
ARG PROCESSOR=x86
ARG SYS_IMG=x86_64
ARG IMG_TYPE=google_apis
ARG BROWSER=android
ENV ANDROID_VERSION=$ANDROID_VERSION \
API_LEVEL=$API_LEVEL \
PROCESSOR=$PROCESSOR \
SYS_IMG=$SYS_IMG \
IMG_TYPE=$IMG_TYPE \
BROWSER=$BROWSER
ENV PATH ${PATH}:${ANDROID_HOME}/build-tools
RUN rm ${ANDROID_HOME}/tools/emulator \
&& ln -s ${ANDROID_HOME}/tools/emulator64-${PROCESSOR} ${ANDROID_HOME}/tools/emulator
RUN echo y | android update sdk --no-ui -a -t android-${API_LEVEL},sys-img-${SYS_IMG}-${IMG_TYPE}-${API_LEVEL}
#==============================================
# Download chrome driver v2.26
# to be able to use chrome browser in emulator
# Issue: https://github.com/butomo1989/docker-android/commit/6406504f944dae73d0a0c5d8e71a17a47dff9b33
#==============================================
RUN wget -nv -O chrome.zip "https://chromedriver.storage.googleapis.com/2.26/chromedriver_linux64.zip" \
&& unzip -x chrome.zip \
&& rm chrome.zip
#================================================
# noVNC Default Configurations
# These Configurations can be changed through -e
#================================================
ENV DISPLAY=:0 \
SCREEN=0 \
SCREEN_WIDTH=1600 \
SCREEN_HEIGHT=900 \
SCREEN_DEPTH=16 \
LOCAL_PORT=5900 \
TARGET_PORT=6080 \
TIMEOUT=1 \
LOG_PATH=/var/log/supervisor
#===============
# Expose Ports
#---------------
# 4723
# Appium port
# 6080
# noVNC port
# 5554
# Emulator port
# 5555
# ADB connection port
#===============
EXPOSE 4723 6080 5554 5555
#======================
# Add Emulator Devices
#======================
COPY devices /root/devices
#===================
# Run docker-appium
#===================
COPY src /root/src
COPY supervisord.conf /root/
RUN chmod -R +x /root/src && chmod +x /root/supervisord.conf
HEALTHCHECK --interval=2s --timeout=40s --retries=1 \
CMD adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done'
CMD /usr/bin/supervisord --configuration supervisord.conf
+18 -46
View File
@@ -1,6 +1,6 @@
FROM ubuntu:16.04 FROM appium/appium:1.6.6-beta-p0
ENV DEBIAN_FRONTEND noninteractive LABEL maintainer "Budi Utomo <budi.ut.1989@gmail.com>"
#============= #=============
# Set WORKDIR # Set WORKDIR
@@ -10,27 +10,15 @@ WORKDIR /root
#================== #==================
# General Packages # General Packages
#------------------ #------------------
# wget
# Network downloader
# unzip
# Unzip zip file
# curl
# Transfer data from or to a server
# xterm # xterm
# Terminal emulator # Terminal emulator
# supervisor # supervisor
# Process manager # Process manager
# openjdk-8-jdk
# Java
# libqt5webkit5
# Web content engine (Fix issue in Android)
# socat # socat
# Port forwarder # Port forwarder
#------------------ #------------------
# NoVNC Packages # NoVNC Packages
#------------------ #------------------
# xvfb
# X virtual framebuffer
# x11vnc # x11vnc
# VNC server for X display # VNC server for X display
# openbox # openbox
@@ -52,15 +40,9 @@ WORKDIR /root
# bridge-utils # bridge-utils
#================== #==================
RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \ RUN apt-get -qqy update && apt-get -qqy install --no-install-recommends \
wget \
unzip \
curl \
xterm \ xterm \
supervisor \ supervisor \
openjdk-8-jdk \
libqt5webkit5 \
socat \ socat \
xvfb \
x11vnc \ x11vnc \
openbox \ openbox \
menu \ menu \
@@ -89,50 +71,36 @@ RUN wget -nv -O noVNC.zip "https://github.com/kanaka/noVNC/archive/${NOVNC_SHA}
&& rm websockify.zip \ && rm websockify.zip \
&& ln noVNC/vnc_auto.html noVNC/index.html && ln noVNC/vnc_auto.html noVNC/index.html
#=====================
# Install Android SDK
#=====================
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64/jre
ENV PATH ${PATH}:${JAVA_HOME}/bin
ENV SDK_VERSION=25.2.3 \
ANDROID_HOME=/root
RUN wget -nv -O android.zip https://dl.google.com/android/repository/tools_r${SDK_VERSION}-linux.zip \
&& unzip android.zip && rm android.zip
ENV PATH ${PATH}:${ANDROID_HOME}/tools
RUN echo y | android update sdk --no-ui -a --filter platform-tools
ENV PATH ${PATH}:${ANDROID_HOME}/platform-tools
#====================================
# Install latest nodejs, npm, appium
#====================================
RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - \
&& apt-get -qqy update && apt-get -qqy install nodejs && rm -rf /var/lib/apt/lists/*
ENV APPIUM_VERSION 1.6.3
RUN npm install -g appium@$APPIUM_VERSION && npm cache clean
#====================== #======================
# Install SDK packages # Install SDK packages
#====================== #======================
ARG ANDROID_VERSION=5.0.1 ARG ANDROID_VERSION=5.0.1
ARG BUILD_TOOL=21.1.2
ARG API_LEVEL=21 ARG API_LEVEL=21
ARG PROCESSOR=x86 ARG PROCESSOR=x86
ARG SYS_IMG=x86_64 ARG SYS_IMG=x86_64
ARG IMG_TYPE=google_apis ARG IMG_TYPE=google_apis
ARG BROWSER=android
ENV ANDROID_VERSION=$ANDROID_VERSION \ ENV ANDROID_VERSION=$ANDROID_VERSION \
BUILD_TOOL=$BUILD_TOOL \
API_LEVEL=$API_LEVEL \ API_LEVEL=$API_LEVEL \
PROCESSOR=$PROCESSOR \ PROCESSOR=$PROCESSOR \
SYS_IMG=$SYS_IMG \ SYS_IMG=$SYS_IMG \
IMG_TYPE=$IMG_TYPE IMG_TYPE=$IMG_TYPE \
RUN echo y | android update sdk --no-ui -a --filter build-tools-${BUILD_TOOL} BROWSER=$BROWSER
ENV PATH ${PATH}:${ANDROID_HOME}/build-tools ENV PATH ${PATH}:${ANDROID_HOME}/build-tools
RUN rm ${ANDROID_HOME}/tools/emulator \ RUN rm ${ANDROID_HOME}/tools/emulator \
&& ln -s ${ANDROID_HOME}/tools/emulator64-${PROCESSOR} ${ANDROID_HOME}/tools/emulator && ln -s ${ANDROID_HOME}/tools/emulator64-${PROCESSOR} ${ANDROID_HOME}/tools/emulator
RUN echo y | android update sdk --no-ui -a -t android-${API_LEVEL},sys-img-${SYS_IMG}-${IMG_TYPE}-${API_LEVEL} RUN echo y | android update sdk --no-ui -a -t android-${API_LEVEL},sys-img-${SYS_IMG}-${IMG_TYPE}-${API_LEVEL}
#==============================================
# Download chrome driver v2.26
# to be able to use chrome browser in emulator
# Issue: https://github.com/butomo1989/docker-android/commit/6406504f944dae73d0a0c5d8e71a17a47dff9b33
#==============================================
RUN wget -nv -O chrome.zip "https://chromedriver.storage.googleapis.com/2.26/chromedriver_linux64.zip" \
&& unzip -x chrome.zip \
&& rm chrome.zip
#================================================ #================================================
# noVNC Default Configurations # noVNC Default Configurations
# These Configurations can be changed through -e # These Configurations can be changed through -e
@@ -172,4 +140,8 @@ COPY devices /root/devices
COPY src /root/src COPY src /root/src
COPY supervisord.conf /root/ COPY supervisord.conf /root/
RUN chmod -R +x /root/src && chmod +x /root/supervisord.conf RUN chmod -R +x /root/src && chmod +x /root/supervisord.conf
HEALTHCHECK --interval=2s --timeout=40s --retries=1 \
CMD adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done'
CMD /usr/bin/supervisord --configuration supervisord.conf CMD /usr/bin/supervisord --configuration supervisord.conf
+1
View File
@@ -0,0 +1 @@
# TODO
+4 -2
View File
@@ -9,10 +9,12 @@ class SimpleAndroidUITests(unittest.TestCase):
desired_caps = { desired_caps = {
'platformName': 'Android', 'platformName': 'Android',
'deviceName': 'Android Emulator', 'deviceName': 'Android Emulator',
'automationName': 'UIAutomator2',
'app': '/root/tmp/sample_apk_debug.apk', 'app': '/root/tmp/sample_apk_debug.apk',
'avd': 'samsung_galaxy_s6_7.1.1' 'browserName': 'android',
'avd': 'nexus_5_7.1.1'
} }
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) self.driver = webdriver.Remote('http://127.0.0.1:4444/wd/hub', desired_caps)
def tearDown(self): def tearDown(self):
self.driver.quit() self.driver.quit()
+17 -3
View File
@@ -1,5 +1,7 @@
import unittest import unittest
from time import sleep
from appium import webdriver from appium import webdriver
@@ -13,12 +15,24 @@ class MSiteChromeAndroidUITests(unittest.TestCase):
'deviceName': 'Android Emulator', 'deviceName': 'Android Emulator',
'appPackage': 'com.android.chrome', 'appPackage': 'com.android.chrome',
'appActivity': 'com.google.android.apps.chrome.Main', 'appActivity': 'com.google.android.apps.chrome.Main',
'avd': 'samsung_galaxy_s6_7.1.1' 'browserName': 'chrome'
} }
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) self.driver = webdriver.Remote('http://127.0.0.1:4444/wd/hub', desired_caps)
def test_open_url(self): def test_open_url(self):
self.driver.get('http://targeturl.com') self.driver.get('http://google.com')
# Handle Welcome Home
self.driver.switch_to.context('NATIVE_APP')
self.driver.find_element_by_id('terms_accept').click()
self.driver.find_element_by_id('negative_button').click()
# Search for Github
self.driver.switch_to.context('CHROMIUM')
search = self.driver.find_element_by_name('q')
search.send_keys('butomo1989 docker-android')
search.submit()
sleep(2)
def tearDown(self): def tearDown(self):
self.driver.quit() self.driver.quit()
@@ -1,5 +1,7 @@
import unittest import unittest
from time import sleep
from appium import webdriver from appium import webdriver
@@ -13,12 +15,17 @@ class MSiteDefaultBrowserAndroidUITests(unittest.TestCase):
'deviceName': 'Android Emulator', 'deviceName': 'Android Emulator',
'appPackage': 'com.android.browser', 'appPackage': 'com.android.browser',
'appActivity': 'com.android.browser.BrowserActivity', 'appActivity': 'com.android.browser.BrowserActivity',
'avd': 'samsung_galaxy_s6_6.0' 'browserName': 'browser'
} }
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) self.driver = webdriver.Remote('http://127.0.0.1:4444/wd/hub', desired_caps)
def test_open_url(self): def test_open_url(self):
self.driver.get('http://targeturl.com') self.driver.get('http://google.com')
search = self.driver.find_element_by_name('q')
search.send_keys('butomo1989 docker-android')
search.submit()
sleep(2)
def tearDown(self): def tearDown(self):
self.driver.quit() self.driver.quit()
Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

+72 -10
View File
@@ -2,7 +2,6 @@
# Bash version should >= 4 to be able to run this script. # Bash version should >= 4 to be able to run this script.
IMAGE="butomo1989/docker-android" IMAGE="butomo1989/docker-android"
LATEST_BUILD_TOOL=25.0.2
if [ -z "$1" ]; then if [ -z "$1" ]; then
read -p "Task (test|build|push|all) : " TASK read -p "Task (test|build|push|all) : " TASK
@@ -93,8 +92,60 @@ get_android_versions
get_processors get_processors
function test() { function test() {
(export ANDROID_HOME=/root && export ANDROID_VERSION=5.0.1 && export API_LEVEL=21 \ # Prepare needed parameter to run tests
&& export PROCESSOR=x86 && export SYS_IMG=x86_64 && export IMG_TYPE=google_apis && nosetests -v) test_android_version=7.1.1
test_api_level=25
test_processor=x86
test_sys_img=x86_64
test_img_type=google_apis
test_browser=chrome
test_image=test_img
test_container=test_con
# Run e2e tests
# E2E tests must be run only for linux OS / x86 image to reduce duration of test execution
if [ "$(uname -s)" == 'Linux' ] && [ "$E2E" = true ]; then
echo "----BUILD TEST IMAGE----"
docker build -t $test_image --build-arg ANDROID_VERSION=$test_android_version \
--build-arg API_LEVEL=$test_api_level --build-arg PROCESSOR=$test_processor --build-arg SYS_IMG=$test_sys_img \
--build-arg IMG_TYPE=$test_img_type --build-arg BROWSER=$test_browser -f docker/Emulator_x86 .
echo "----REMOVE OLD TEST CONTAINER----"
docker kill $test_container && docker rm $test_container
echo "----PREPARE CONTAINER----"
docker run --privileged -d -p 4723:4723 -p 6080:6080 -e APPIUM=True -e DEVICE="Samsung Galaxy S6" --name $test_container $test_image
docker cp example/sample_apk $test_container:/root/tmp
attempt=0
while [ ${attempt} -le 10 ]; do
attempt=$(($attempt + 1))
output=$(docker ps | grep healthy | grep test_con | wc -l)
if [[ "$output" == 1 ]]; then
echo "Emulator is ready."
break
else
echo "Waiting 10 seconds for emulator to be ready (attempt: $attempt)"
sleep 10
fi
if [[ $attempt == 10 ]]; then
echo "Failed!"
exit 1
fi
done
echo "----RUN E2E TESTS----"
nosetests src/tests/e2e -v
echo "----REMOVE TEST CONTAINER----"
docker kill $test_container && docker rm $test_container
fi
# Run unit tests (After e2e test to get coverage result)
echo "----UNIT TESTS----"
(export ANDROID_HOME=/root && export ANDROID_VERSION=$test_android_version && export API_LEVEL=$test_api_level \
&& export PROCESSOR=$test_processor && export SYS_IMG=$test_sys_img && export IMG_TYPE=$test_img_type \
&& nosetests src/tests/unit -v)
} }
function build() { function build() {
@@ -103,12 +154,24 @@ function build() {
# Build docker image(s) # Build docker image(s)
for p in "${processors[@]}"; do for p in "${processors[@]}"; do
if [ "$p" == "x86" ]; then
FILE_NAME=docker/Emulator_x86
else
FILE_NAME=docker/Emulator_arm
fi
for v in "${versions[@]}"; do for v in "${versions[@]}"; do
# Find image type # Find image type and default web browser
if [ "$v" == "5.0.1" ] || [ "$v" == "5.1.1" ]; then if [ "$v" == "5.0.1" ] || [ "$v" == "5.1.1" ]; then
IMG_TYPE=android IMG_TYPE=android
BROWSER=browser
elif [ "$v" == "6.0" ]; then
# It is because there is no ARM EABI v7a System Image for 6.0
IMG_TYPE=google_apis
BROWSER=browser
else else
IMG_TYPE=google_apis IMG_TYPE=google_apis
BROWSER=chrome
fi fi
echo "[BUILD] IMAGE TYPE: $IMG_TYPE" echo "[BUILD] IMAGE TYPE: $IMG_TYPE"
level=${list_of_levels[$v]} level=${list_of_levels[$v]}
@@ -118,12 +181,11 @@ function build() {
image_version="$IMAGE-$p-$v:$RELEASE" image_version="$IMAGE-$p-$v:$RELEASE"
image_latest="$IMAGE-$p-$v:latest" image_latest="$IMAGE-$p-$v:latest"
echo "[BUILD] Image name: $image_version and $image_latest" echo "[BUILD] Image name: $image_version and $image_latest"
docker build -t $image_version --build-arg ANDROID_VERSION=$v --build-arg BUILD_TOOL=$LATEST_BUILD_TOOL \ echo "[BUILD] Dockerfile: $FILE_NAME"
--build-arg API_LEVEL=$level --build-arg PROCESSOR=$p --build-arg SYS_IMG=$sys_img \ docker build -t $image_version --build-arg ANDROID_VERSION=$v --build-arg API_LEVEL=$level \
--build-arg IMG_TYPE=$IMG_TYPE . --build-arg PROCESSOR=$p --build-arg SYS_IMG=$sys_img --build-arg IMG_TYPE=$IMG_TYPE \
docker build -t $image_latest --build-arg ANDROID_VERSION=$v --build-arg BUILD_TOOL=$LATEST_BUILD_TOOL \ --build-arg BROWSER=$BROWSER -f $FILE_NAME .
--build-arg API_LEVEL=$level --build-arg PROCESSOR=$p --build-arg SYS_IMG=$sys_img \ docker tag $image_version $image_latest
--build-arg IMG_TYPE=$IMG_TYPE .
done done
done done
} }
+2
View File
@@ -1,3 +1,5 @@
selenium==3.3.1
Appium-Python-Client==0.24
coverage==4.2 coverage==4.2
mock==2.0.0 mock==2.0.0
nose==1.3.7 nose==1.3.7
+1
View File
@@ -2,5 +2,6 @@ import os
ROOT = '/root' ROOT = '/root'
WORKDIR = os.path.dirname(__file__) WORKDIR = os.path.dirname(__file__)
CHROME_DRIVER = os.path.join(ROOT, 'chromedriver')
CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json') CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json')
LOGGING_FILE = os.path.join(WORKDIR, 'logging.conf') LOGGING_FILE = os.path.join(WORKDIR, 'logging.conf')
+21 -11
View File
@@ -5,7 +5,7 @@ import logging
import os import os
import subprocess import subprocess
from src import CONFIG_FILE, ROOT from src import CONFIG_FILE, ROOT, CHROME_DRIVER
from src import log from src import log
log.init() log.init()
@@ -26,7 +26,7 @@ def get_or_raise(env: str) -> str:
return env_value return env_value
def str_to_bool(str: str) -> bool: def convert_str_to_bool(str: str) -> bool:
""" """
Convert string to boolean. Convert string to boolean.
@@ -101,23 +101,31 @@ def appium_run(avd_name: str):
""" """
cmd = 'appium' cmd = 'appium'
grid_connect = str_to_bool(str(os.getenv('CONNECT_TO_GRID', False))) default_web_browser = os.getenv('BROWSER')
if default_web_browser == 'chrome':
cmd += ' --chromedriver-executable {driver}'.format(driver=CHROME_DRIVER)
grid_connect = convert_str_to_bool(str(os.getenv('CONNECT_TO_GRID', False)))
logger.info('Connect to selenium grid? {connect}'.format(connect=grid_connect)) logger.info('Connect to selenium grid? {connect}'.format(connect=grid_connect))
if grid_connect: if grid_connect:
local_ip = os.popen('ifconfig eth0 | grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\'').read().strip()
try: try:
appium_host = os.getenv('APPIUM_HOST', '127.0.0.1') mobile_web_test = convert_str_to_bool(str(os.getenv('MOBILE_WEB_TEST', False)))
appium_host = os.getenv('APPIUM_HOST', local_ip)
appium_port = int(os.getenv('APPIUM_PORT', 4723)) appium_port = int(os.getenv('APPIUM_PORT', 4723))
selenium_host = os.getenv('SELENIUM_HOST', '172.17.0.1') selenium_host = os.getenv('SELENIUM_HOST', '172.17.0.1')
selenium_port = int(os.getenv('SELENIUM_PORT', 4444)) selenium_port = int(os.getenv('SELENIUM_PORT', 4444))
create_node_config(avd_name, appium_host, appium_port, selenium_host, selenium_port) browser_name = default_web_browser if mobile_web_test else 'android'
create_node_config(avd_name, browser_name, appium_host, appium_port, selenium_host, selenium_port)
cmd += ' --nodeconfig {file}'.format(file=CONFIG_FILE) cmd += ' --nodeconfig {file}'.format(file=CONFIG_FILE)
except ValueError as v_err: except ValueError as v_err:
logger.error(v_err) logger.error(v_err)
titel = 'Appium Server' title = 'Appium Server'
subprocess.check_call('xterm -T "{titel}" -n "{titel}" -e \"{cmd}\"'.format(titel=titel, cmd=cmd), shell=True) subprocess.check_call('xterm -T "{title}" -n "{title}" -e \"{cmd}\"'.format(title=title, cmd=cmd), shell=True)
def create_node_config(avd_name: str, appium_host: str, appium_port: int, selenium_host: str, selenium_port: int): def create_node_config(avd_name: str, browser_name: str, appium_host: str, appium_port: int, selenium_host: str,
selenium_port: int):
""" """
Create custom node config file in json format to be able to connect with selenium server. Create custom node config file in json format to be able to connect with selenium server.
@@ -133,7 +141,8 @@ def create_node_config(avd_name: str, appium_host: str, appium_port: int, seleni
'platform': 'Android', 'platform': 'Android',
'platformName': 'Android', 'platformName': 'Android',
'version': ANDROID_VERSION, 'version': ANDROID_VERSION,
'browserName': avd_name, 'browserName': browser_name,
'deviceName': avd_name,
'maxInstances': 1, 'maxInstances': 1,
} }
], ],
@@ -148,7 +157,8 @@ def create_node_config(avd_name: str, appium_host: str, appium_port: int, seleni
'register': True, 'register': True,
'registerCycle': 5000, 'registerCycle': 5000,
'hubHost': selenium_host, 'hubHost': selenium_host,
'hubPort': selenium_port 'hubPort': selenium_port,
'unregisterIfStillDownAfter': 120000
} }
} }
logger.info('Appium node config: {config}'.format(config=config)) logger.info('Appium node config: {config}'.format(config=config))
@@ -170,7 +180,7 @@ def run():
cmd = 'emulator -avd {name}'.format(name=avd_name) cmd = 'emulator -avd {name}'.format(name=avd_name)
subprocess.Popen(cmd.split()) subprocess.Popen(cmd.split())
appium = str_to_bool(str(os.getenv('APPIUM', False))) appium = convert_str_to_bool(str(os.getenv('APPIUM', False)))
if appium: if appium:
logger.info('Run appium server...') logger.info('Run appium server...')
appium_run(avd_name) appium_run(avd_name)
-54
View File
@@ -1,54 +0,0 @@
"""Unit test to test app."""
import os
from unittest import TestCase
import mock
from src import app
class TestApp(TestCase):
"""Unit test class to test other methods in the app."""
def test_valid_env(self):
key = 'ENV_1'
os.environ[key] = 'test'
app.get_or_raise(key)
del os.environ[key]
def test_invalid_env(self):
with self.assertRaises(RuntimeError):
app.get_or_raise('ENV_2')
def test_valid_bool(self):
self.assertEqual(app.str_to_bool('True'), True)
self.assertEqual(app.str_to_bool('t'), True)
self.assertEqual(app.str_to_bool('1'), True)
self.assertEqual(app.str_to_bool('YES'), True)
def test_invalid_bool(self):
self.assertEqual(app.str_to_bool(''), False)
self.assertEqual(app.str_to_bool('test'), False)
def test_invalid_format(self):
self.assertEqual(app.str_to_bool(True), None)
@mock.patch('src.app.prepare_avd')
@mock.patch('subprocess.Popen')
def test_run_with_appium(self, mocked_avd, mocked_subprocess):
with mock.patch('src.app.appium_run') as mocked_appium:
os.environ['APPIUM'] = str(True)
app.run()
self.assertTrue(mocked_avd.called)
self.assertTrue(mocked_subprocess.called)
self.assertTrue(mocked_appium.called)
@mock.patch('src.app.prepare_avd')
@mock.patch('subprocess.Popen')
def test_run_withhout_appium(self, mocked_avd, mocked_subprocess):
with mock.patch('src.app.appium_run') as mocked_appium:
os.environ['APPIUM'] = str(False)
app.run()
self.assertTrue(mocked_avd.called)
self.assertTrue(mocked_subprocess.called)
self.assertFalse(mocked_appium.called)
View File
+29
View File
@@ -0,0 +1,29 @@
"""e2e test to test sample android application inside docker-android"""
from unittest import TestCase
from appium import webdriver
class TestE2EAndroidApk(TestCase):
def setUp(self):
desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
'automationName': 'UIAutomator2',
'app': '/root/tmp/sample_apk_debug.apk'
}
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
def test_calculation(self):
text_fields = self.driver.find_elements_by_class_name('android.widget.EditText')
text_fields[0].send_keys(4)
text_fields[1].send_keys(6)
btn_calculate = self.driver.find_element_by_class_name('android.widget.Button')
btn_calculate.click()
self.assertEqual('10', text_fields[2].text)
def tearDown(self):
self.driver.quit()
+34
View File
@@ -0,0 +1,34 @@
"""e2e test to test chrome application inside docker-android"""
from unittest import TestCase
from appium import webdriver
class TestE2EChrome(TestCase):
def setUp(self):
desired_caps = {
'platformName': 'Android',
'deviceName': 'Android Emulator',
'appPackage': 'com.android.chrome',
'appActivity': 'com.google.android.apps.chrome.Main',
'browserName': 'chrome'
}
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
def test_open_url(self):
self.driver.get('http://google.com')
# Handle Welcome Home
self.driver.switch_to.context('NATIVE_APP')
self.driver.find_element_by_id('terms_accept').click()
self.driver.find_element_by_id('negative_button').click()
# Search for Github
self.driver.switch_to.context('CHROMIUM')
search = self.driver.find_element_by_name('q')
search.send_keys('butomo1989 docker-android')
search.submit()
def tearDown(self):
self.driver.quit()
View File
+54
View File
@@ -0,0 +1,54 @@
"""Unit test to test app."""
import os
from unittest import TestCase
import mock
from src import app
class TestApp(TestCase):
"""Unit test class to test other methods in the app."""
def test_valid_env(self):
key = 'ENV_1'
os.environ[key] = 'test'
app.get_or_raise(key)
del os.environ[key]
def test_invalid_env(self):
with self.assertRaises(RuntimeError):
app.get_or_raise('ENV_2')
def test_valid_bool(self):
self.assertEqual(app.convert_str_to_bool('True'), True)
self.assertEqual(app.convert_str_to_bool('t'), True)
self.assertEqual(app.convert_str_to_bool('1'), True)
self.assertEqual(app.convert_str_to_bool('YES'), True)
def test_invalid_bool(self):
self.assertEqual(app.convert_str_to_bool(''), False)
self.assertEqual(app.convert_str_to_bool('test'), False)
def test_invalid_format(self):
self.assertEqual(app.convert_str_to_bool(True), None)
@mock.patch('src.app.prepare_avd')
@mock.patch('subprocess.Popen')
def test_run_with_appium(self, mocked_avd, mocked_subprocess):
with mock.patch('src.app.appium_run') as mocked_appium:
os.environ['APPIUM'] = str(True)
app.run()
self.assertTrue(mocked_avd.called)
self.assertTrue(mocked_subprocess.called)
self.assertTrue(mocked_appium.called)
@mock.patch('src.app.prepare_avd')
@mock.patch('subprocess.Popen')
def test_run_withhout_appium(self, mocked_avd, mocked_subprocess):
with mock.patch('src.app.appium_run') as mocked_appium:
os.environ['APPIUM'] = str(False)
app.run()
self.assertTrue(mocked_avd.called)
self.assertTrue(mocked_subprocess.called)
self.assertFalse(mocked_appium.called)
@@ -14,6 +14,14 @@ class TestAppium(TestCase):
os.environ['CONNECT_TO_GRID'] = str(True) os.environ['CONNECT_TO_GRID'] = str(True)
self.avd_name = 'test_avd' self.avd_name = 'test_avd'
@mock.patch('subprocess.check_call')
def test_chrome_driver(self, mocked_subprocess):
os.environ['CONNECT_TO_GRID'] = str(False)
os.environ['BROWSER'] = 'chrome'
self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name)
self.assertTrue(mocked_subprocess.called)
@mock.patch('subprocess.check_call') @mock.patch('subprocess.check_call')
def test_without_selenium_grid(self, mocked_subprocess): def test_without_selenium_grid(self, mocked_subprocess):
os.environ['CONNECT_TO_GRID'] = str(False) os.environ['CONNECT_TO_GRID'] = str(False)
@@ -21,30 +29,36 @@ class TestAppium(TestCase):
app.appium_run(self.avd_name) app.appium_run(self.avd_name)
self.assertTrue(mocked_subprocess.called) self.assertTrue(mocked_subprocess.called)
@mock.patch('os.popen')
@mock.patch('subprocess.check_call') @mock.patch('subprocess.check_call')
def test_with_selenium_grid(self, mocked_subprocess): def test_with_selenium_grid(self, mocked_os, mocked_subprocess):
with mock.patch('src.app.create_node_config') as mocked_config: with mock.patch('src.app.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called) self.assertFalse(mocked_config.called)
self.assertFalse(mocked_os.called)
self.assertFalse(mocked_subprocess.called) self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name) app.appium_run(self.avd_name)
self.assertTrue(mocked_config.called) self.assertTrue(mocked_config.called)
self.assertTrue(mocked_os.called)
self.assertTrue(mocked_subprocess.called) self.assertTrue(mocked_subprocess.called)
@mock.patch('os.popen')
@mock.patch('subprocess.check_call') @mock.patch('subprocess.check_call')
def test_invalid_integer(self, mocked_subprocess): def test_invalid_integer(self, mocked_os, mocked_subprocess):
os.environ['APPIUM_PORT'] = 'test' os.environ['APPIUM_PORT'] = 'test'
with mock.patch('src.app.create_node_config') as mocked_config: with mock.patch('src.app.create_node_config') as mocked_config:
self.assertFalse(mocked_config.called) self.assertFalse(mocked_config.called)
self.assertFalse(mocked_os.called)
self.assertFalse(mocked_subprocess.called) self.assertFalse(mocked_subprocess.called)
app.appium_run(self.avd_name) app.appium_run(self.avd_name)
self.assertFalse(mocked_config.called) self.assertFalse(mocked_config.called)
self.assertTrue(mocked_os.called)
self.assertTrue(mocked_subprocess.called) self.assertTrue(mocked_subprocess.called)
self.assertRaises(ValueError) self.assertRaises(ValueError)
def test_config_creation(self): def test_config_creation(self):
from src import CONFIG_FILE from src import CONFIG_FILE
self.assertFalse(os.path.exists(CONFIG_FILE)) self.assertFalse(os.path.exists(CONFIG_FILE))
app.create_node_config('test', '127.0.0.1', 4723, '127.0.0.1', 4444) app.create_node_config('test', 'android', '127.0.0.1', 4723, '127.0.0.1', 4444)
self.assertTrue(os.path.exists(CONFIG_FILE)) self.assertTrue(os.path.exists(CONFIG_FILE))
os.remove(CONFIG_FILE) os.remove(CONFIG_FILE)
@@ -52,3 +66,5 @@ class TestAppium(TestCase):
del os.environ['CONNECT_TO_GRID'] del os.environ['CONNECT_TO_GRID']
if os.getenv('APPIUM_PORT'): if os.getenv('APPIUM_PORT'):
del os.environ['APPIUM_PORT'] del os.environ['APPIUM_PORT']
if os.getenv('BROWSER'):
del os.environ['BROWSER']