Tuesday, November 22, 2016

Running Mobile Web (Chrome) Tests on Android Virtual Device (Genymotion) using Appium

Requirements:
- OS X El Capitan (10.11.6)
- Android SDK (with tools and platform-tools)
- JDK 1.8
- Node.js v7.0.0 (brew install node)
- appium 1.6.0 (npm install -g appium)
- VirtualBox
Genymotion (2.8.0) virtual device (requires an account - free for personal use)
- chrome.apk
- chromedriver (brew install chromedriver)

Steps:
- Start the Android Genymotion virtual device.



- Verify appium installation/setup using the ff command:
$ appium-doctor --android



- Start appium using the ff command:
$ appium --default-capabilities android.json

where android.json:
{
"app":"chrome",
"newCommandTimeout":600,
"chromedriverExecutable":"/usr/local/bin/chromedriver"
}




- Run the following code:


import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

public class AppiumAndroid {
 private static WebDriver driver;

 public static void main(String[] args) throws MalformedURLException, InterruptedException {
  String hubUrl = "http://127.0.0.1:4723/wd/hub";

  DesiredCapabilities capabilities = new DesiredCapabilities();
  capabilities.setCapability("deviceName", "Android Emulator");
  capabilities.setCapability("platformName", "Android");
  capabilities.setCapability("platformVersion", "7.0");
  capabilities.setCapability("browserName", "Chrome");
  capabilities.setCapability("app", "/Users/blago/Development/appium/chrome.apk");
  // http://www.apkmirror.com/apk/google-inc/chrome/chrome-54-0-2840-68-release/chrome-54-0-2840-68-3-android-apk-download/
  driver = new RemoteWebDriver(new URL(hubUrl), capabilities);
  driver.manage().timeouts().implicitlyWait(120, TimeUnit.SECONDS);

  String[] searchTerms = new String[] { "Bernard Lago Blogspot", "Bernard Lago Youtube",
    "Bernard Lago Google Plus" };

  for (int i = 0; i < searchTerms.length; i++) {
   googleSearch(searchTerms[i]);
   Thread.sleep(3000);
   getInfoUsingResultIndex(1); // 1 for the first link, 2 for 2nd
  }

  driver.quit();
  System.out.println("Done.");
 }

 public static void googleSearch(String searchString) {
  System.out.println("Search term: " + searchString);
  driver.navigate().to("https://www.google.com.ph/#q=" + searchString);
 }

 public static void getInfoUsingResultIndex(int index) {
  String firstLinkLocator = String.format("div.g:nth-child(%s) h3.r a", index);
  String firstCiteLocator = String.format("div.g:nth-child(%s) cite", index);
  WebElement firstLink = driver.findElement(By.cssSelector(firstLinkLocator));
  System.out.println("First Link URL: " + driver.findElement(By.cssSelector(firstCiteLocator)).getText());
  System.out.println("First link Page Title: " + firstLink.getText());
 }

}


Here's the screenshot of the actual execution.



You can also watch it here.

That's it. Thanks for visiting!

Wednesday, October 26, 2016

Running Mobile Web Tests on iOS 10 Device (Simulator) using Appium iOS driver, backed by Apple XCUITest (Xcode 8)


Requirements:
- OS X El Capitan (10.11.6)
- Xcode 8 (with command line tools)
- JDK 1.8
- Node.js v6.5.0 (brew install node)
- appium 1.6.0 (npm install -g appium)
Other appium node_modules (appium-doctor, appium-xcuitest-driver)


As a background and for those familiar with appium, UIAutomation - which was being used by Appium for its previous version of running tests against iOS devices - is already deprecated by Apple.

With version 1.6.0,  Appium has added support to target Apple's new XCUITest framework, which includes support for iOS 10 / Xcode 8.

Here's the github link for appium-xcuitest-driver.

Steps:

- Verify appium installation/setup using the ff command:
$ appium-doctor --ios




- Start appium using the ff command:
$ appium --default-capabilities ios.json

where ios.json:
{
"app":"safari",
"newCommandTimeout":600
}





- Run the following code:

import java.net.MalformedURLException;
import java.net.URL;

import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

public class AppiumIOS {
 private final static Logger logger = Logger.getLogger(AppiumIOS.class);
 static WebDriver driver;
 
 public static void main(String[] args) throws MalformedURLException, InterruptedException {
  String hubUrl = "http://127.0.0.1:4723/wd/hub";
  String iOSBrowserName = "Safari";
  String iOSDeviceName = "iPhone Simulator";
  String iOSVersion = "10.0";

  DesiredCapabilities capabilities = new DesiredCapabilities();
  capabilities.setCapability("deviceName", iOSDeviceName);
  capabilities.setCapability("platformName", "iOS");
  capabilities.setCapability("platformVersion", iOSVersion);
  capabilities.setCapability("browserName", iOSBrowserName);
  capabilities.setCapability("automationName", "XCUITest");
  driver = new RemoteWebDriver(new URL(hubUrl), capabilities);
  
  String[] searchTerms = new String[] { 
   "Bernard Lago Blogspot", 
   "Bernard Lago Youtube",
   "Bernard Lago Google Plus" 
  };

  for (int i = 0; i < searchTerms.length; i++) {
   googleSearch(searchTerms[i]);
   Thread.sleep(3000);
   getInfoUsingResultIndex(1);  // 1 for the first link, 2 for 2nd etc...
  }
  
  driver.quit();

 }
 
 public static void googleSearch(String searchString) {
  logger.info("Search term: " + searchString);
  driver.navigate().to("https://www.google.com.ph/#q=" + searchString);
 }

 public static void getInfoUsingResultIndex(int index) {
  String firstLinkLocator = String.format("div.g:nth-child(%s) h3.r a", index);
  String firstCiteLocator = String.format("div.g:nth-child(%s) cite", index);
  WebElement firstLink = driver.findElement(By.cssSelector(firstLinkLocator));
  logger.info("First Link URL: "+ driver.findElement(By.cssSelector(firstCiteLocator)).getText());
  logger.info("First link Page Title: " + firstLink.getText());
 }

}

Here's the screenshot of the actual execution.



That's it. Thanks for visiting!

Friday, October 7, 2016

Analyzing free proxies from public websites


Recently,  I've been scraping free proxies from public websites like hidemyass and incloak.

I added another source to the scraper to pull proxies from my-proxy.com and stored the proxies to my local MySQL database.

Here's the table structure:

+------------------+--------------+------+-----+-------------------+----------------+
| Field            | Type         | Null | Key | Default           | Extra          |
+------------------+--------------+------+-----+-------------------+----------------+
| id               | int(11)      | NO   | PRI | NULL              | auto_increment |
| scrape_timestamp | datetime     | YES  |     | CURRENT_TIMESTAMP |                |
| last_update      | varchar(255) | YES  |     | NULL              |                |
| ip_address       | varchar(255) | YES  |     | NULL              |                |
| port             | varchar(255) | YES  |     | NULL              |                |
| country          | varchar(255) | YES  |     | NULL              |                |
| speed            | int(11)      | YES  |     | NULL              |                |
| connection_time  | int(11)      | YES  |     | NULL              |                |
| type             | varchar(255) | YES  |     | NULL              |                |
| anonymity        | varchar(255) | YES  |     | NULL              |                |
| source           | varchar(255) | YES  |     | NULL              |                |
| tested           | tinyint(1)   | YES  |     | NULL              |                |
| verified         | tinyint(1)   | YES  |     | NULL              |                |
| used             | tinyint(1)   | YES  |     | NULL              |                |
| date_used        | datetime     | YES  |     | NULL              |                |
+------------------+--------------+------+-----+-------------------+----------------+

Here's the data gathered after using whatismyipaddress.com for verifying the list.

SELECT t1.source,
       t2.total,
       t1.verified,
       (SELECT Truncate(( t1.verified / t2.total * 100 ) + 0.06, 0)) AS
       percent_working
FROM   (SELECT source,
               Count(*) AS verified
        FROM   proxies
        WHERE  verified = 1
        GROUP  BY source) AS t1
       LEFT JOIN (SELECT source,
                         Count(*) AS total
                  FROM   proxies
                  GROUP  BY source) AS t2
              ON t1.source = t2.source; 



+-----------+-------+----------+-----------------+
| source    | total | verified | percent_working |
+-----------+-------+----------+-----------------+
| hidemyass |   309 |       74 |              24 |
| incloak   |   436 |       78 |              17 |
| my-proxy  |   900 |       55 |               6 |
+-----------+-------+----------+-----------------+

Graph generated here: https://jsfiddle.net/L3vnuyx6/


my-proxy has the highest number of free proxies. It also has the lowest number of verified working proxies.

hidemyass has the highest percentage (verified / total)

incloak has the highest number of verified proxies in terms of count.

Saturday, August 6, 2016

Automating Mobile Web Gestures Using RSpec and Appium and Ruby's Touch Action library.



The following libraries are used:
ruby 2.3.0
rspec-core (3.4.0)
touch_action (1.3.3)
appium_lib (8.0.2)

The code below will automate the following mobile web gestures using Chrome on a real mobile device:

  tap
  double tap
  swipe left to right
  swipe right to left
  swipe up
  swipe down
  move/drag
  rotate
  pinch


 require 'rubygems'  
 require 'rspec/core'  
 require 'touch_action'  
 require 'appium_lib'  

 describe "Mobile Web Gestures" do  
 
      # install android sdk with platform-tools and build-tools  
      # run 'adb devices' to list deviceName  
      # start appium on a terminal  
      # on another terminal, run the following command once.  
      # bundle install   
      # to run the tests, use the ff:  
      # rspec mobile_web_gestures.rb --format documentation  

      before(:all) do  
           desired_caps = {  
                caps: {  
                     platformName: 'Android',  
                     versionNumber: '5.0.2',  
                     deviceName: '<device_id>',  
                     device: 'Android',  
                     browserName: 'Chrome'  
                }  
           }  
           @driver = Appium::Driver.new(desired_caps).start_driver  
           @driver.manage.timeouts.implicit_wait = 120  
           base_url = 'http://hammerjs.github.io'  
           @driver.navigate.to(base_url + '/')  
           sleep 2  
           @driver.execute_script("window.stop")  
           device = @driver.find_element(:css, 'div.device')  
           @driver.execute_script("arguments[0].scrollIntoView(true);", device)  
           @element = @driver.find_element(:id, 'hitarea')  
      end 
 
      after(:all) do  
           @driver.quit  
      end  

      it "performs mobile gestures" do  
           sleep 2  
           @element.touch_action(:tap)  
           sleep 2  
           @element.touch_action(:doubletap)  
           sleep 2  
           @element.touch_action(:flick, axis: "x", distance: 100, duration: 50)  
           sleep 2  
           @element.touch_action(:flick, axis: "y", distance: 100, duration: 500)  
           sleep 2  
           @element.touch_action(:move, xdist: 70, ydist: -50, duration: 500)  
           sleep 2  
           @element.touch_action(:rotate, {rotation: -75})  
           sleep 2  
           @element.touch_action(:rotate, {rotation: 0})  
           sleep 2  
           @element.touch_action(:pinch, r1: 50, r2: 100)  
      end  

 end  

See it in action here.

Thanks for visiting!

Saturday, February 6, 2016

OS X Setup and iOS Device Configuration for Mobile Web Test Automation Using Free iOS Provisioning Profile

Requirements:

  • OS X Yosemite 10.10.5
  • Xcode 7.2.1
  • iOS Device (9.2.1)
  • Homebrew
  • Git
  • SafariLauncher
  • Appium (1.4.16)
  • Apple ID with *Provisioning Profile (iOS Development)
*Note: Free iOS Development Provisioning Profile can be created via Xcode 7

I. Create iOS Development Provisioning Profile for SafariLauncher

  • Clone https://github.com/budhash/SafariLauncher.git
  • Open the project in Xcode
    2016-02-06_1915
  • Open Xcode > Preferences > Accounts
  • Add an Apple ID
  • Click the apple id and click View Details
  • Create iOS Development Signing Identities
ios_development_account

II. Create Provisioning Profile for SafariLauncher

  • Using the same xcode project, provide ‘Bundle Identifier’ value.
    i.e.
    com.github.blago.SafariLauncher
  • Fix the issue with Provisioning Profile
    2016-02-06_1551
  • After fixing the issue, provide deployment information
    2016-02-06_1227
  • For Xcode 7.2.1, click Build Settings and edit Code Signing section
    2016-02-06_1230
  • Build and run SafariLauncher on the iOS Device
    2016-02-06_1258
  • Trust the developer of the app in the iOS Device.
    Go to Settings > General > Device Management
    Screenshot 2016.02.06 13.35.13

III. Build SafariLauncher for Real Devices (using appium source)

  • Clone https://github.com/appium/appium.git
    • SHA-1 hash used: 54c2b4e6500eddbca4bb3047116ceb8d177f7294
    • git checkout 54c2b4e6500eddbca4bb3047116ceb8d177f7294
  • Go to the appium directory (clone) and edit reset.sh then provide the BUNDLE_ID used in the provisioning profile
    2016-02-06_1247
  • Run the command inside the appium directory
    ./reset.sh --ios --real-safari --verbose
    
    
    Sample Output
    2016-02-06_1244


IV. Install Homebrew Packages 

  • Do the following command
    brew update
    brew install libimobiledevice libplist libtasn1 usbmuxd openssl ideviceinstaller
    brew install ios-webkit-debug-proxy
    brew install node


V. iOS Device Configuration

  • Enable UI Automation (Settings > Developer)
    Screenshot 2016.02.06 13.06.04
  • Turn on Safari Web Inspector  (Settings > Safari > Advanced)
    Screenshot 2016.02.06 13.07.15


VI. Appium Server Configuration

  • Start ios_webkit_debug_proxy
    ios_webkit_debug_proxy -c <iOS_SERIAL_NUMBER>:27753
    
    
  • Start the sever inside the appium directory
    node . -U <iOS_SERIAL_NUMBER> --app 'safari' --command-timeout 120
    
    
    Note:
    iOS_SERIAL_NUMBER can be found in
    About This Mac > Overview > System Report > Hardware > USB
    2016-02-06_1317

VII. Code Configuration


 String hubUrl = "http://127.0.0.1:4723/wd/hub";  
 String iOSBrowserName = "Safari";  
 String iOSDeviceName = "<iOS_SERIAL_NUMBER>";  
 String iOSVersion = 9.2;  

 DesiredCapabilities capabilities = new DesiredCapabilities();  
 capabilities.setCapability("deviceName", iOSDeviceName);  
 capabilities.setCapability("platformName", "iOS");  
 capabilities.setCapability("platformVersion", iOSVersion);  
 capabilities.setCapability("browserName", iOSBrowserName);  
 WebDriver driver = new RemoteWebDriver(new URL(hubUrl), capabilities); 


VIII. Possible Runtime Errors

  • FBSOpenApplicationErrorDomain error

    info: [debug] [INST STDERR] Instruments Trace Error : Target failed to run: The operation couldn’t be completed. (FBSOpenApplicationErrorDomain error 3.) : Failed to launch process with bundle identifier ‘com.github.blago.SafariLauncher'

    Solution:

    Trust the developer who signed the SafariLauncher app (Settings > General > Device Management)
    Screenshot 2016.02.06 13.35.13
  • ideviceinstaller error

    Error: Command failed: /bin/sh -c ideviceinstaller

    Solution:

    Select the target device in Xcode for the SafariLauncher then restart the appium node.2016-02-06_1620
    2016-02-06_1626

IX. Sample Appium Server Output with iOS Device Screen

  • On standby, waiting for test to run
    2016-02-06_1740
  • Test executed, SafariLauncher started on iOS device.
    2016-02-06_1734
  • Actual test being executed, application under test is m.facebook.com
    2016-02-06_1735

Click here to watch the execution.

Thanks for visiting my blog!