Skip to content

Commit

Permalink
Add Appium test to find locator by image
Browse files Browse the repository at this point in the history
  • Loading branch information
eyaly committed Apr 28, 2024
1 parent cb9982b commit 665417f
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 5 deletions.
1 change: 1 addition & 0 deletions appium/appium-app/appium-app-examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ This folder contains Appium examples
* [Upload and download file](./src/test/java/com/examples/up_download_file)
* [Using deep link](./src/test/java/com/examples/deep_link)
* [Test your allowlisting app](./src/test/java/com/examples/allowlist)
* [Using Appium Image Selectors](./src/test/java/com/examples/find_by_image)
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.examples.find_by_image;

import com.helpers.SauceAppiumTestWatcher;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

import static com.helpers.Constants.*;
import static org.assertj.core.api.Assertions.assertThat;

/**
* Android Native App Tests
*/
public class AndroidFindByImageTest {

By productsScreenLocator = By.id("com.saucelabs.mydemoapp.android:id/productTV");
By productItemTitleLocator = By.id("com.saucelabs.mydemoapp.android:id/productTV");


@Rule
public TestName name = new TestName();

//This rule allows us to set test status with Junit
@Rule
public SauceAppiumTestWatcher resultReportingTestWatcher = new SauceAppiumTestWatcher();

private AndroidDriver driver;

@Before
public void setup() throws MalformedURLException {
System.out.println("Sauce Android Native App - Before hook");
MutableCapabilities capabilities = new MutableCapabilities();
MutableCapabilities sauceOptions = new MutableCapabilities();
URL url;

switch (region) {
case "us":
url = new URL(SAUCE_US_URL);
System.out.println("Sauce REGION US");
break;
case "eu":
default:
url = new URL(SAUCE_EU_URL);
System.out.println("Sauce REGION EU");
break;
}

// For all capabilities please check
// https://appium.io/docs/en/2.0/guides/caps/
// Use the platform configuration https://saucelabs.com/platform/platform-configurator#/
// to find the emulators/real devices names, OS versions and appium versions you can use for your testings
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("appium:automationName", "UiAutomator2");
if (rdc.equals("true")) {
capabilities.setCapability("appium:deviceName", "^Samsung.*");
capabilities.setCapability("appium:platformVersion", "1[2-4]");
sauceOptions.setCapability("resigningEnabled", true);
sauceOptions.setCapability("sauceLabsNetworkCaptureEnabled", true);
}
else {
capabilities.setCapability("appium:deviceName", "Android GoogleAPI Emulator");
capabilities.setCapability("appium:platformVersion", "13");
}
String appName = "SauceLabs-Demo-App.apk";
capabilities.setCapability("appium:app", "storage:filename=" +appName);

// Sauce capabilities
sauceOptions.setCapability("name", name.getMethodName());
sauceOptions.setCapability("appiumVersion", "latest");
sauceOptions.setCapability("build", "myApp-job-findImage-1");
List<String> tags = Arrays.asList("sauceDemo", "Android", "FindByImage");
sauceOptions.setCapability("tags", tags);
sauceOptions.setCapability("username", System.getenv("SAUCE_USERNAME"));
sauceOptions.setCapability("accessKey", System.getenv("SAUCE_ACCESS_KEY"));
capabilities.setCapability("sauce:options", sauceOptions);

MutableCapabilities settingsOptions = new MutableCapabilities();
settingsOptions.setCapability("imageMatchThreshold", 0.4);
settingsOptions.setCapability("getMatchedImageResult", true);

capabilities.setCapability("appium:settings", settingsOptions);

try {
driver = new AndroidDriver(url, capabilities);
} catch (Exception e){
System.out.println("Error to create Android Driver: " + e.getMessage());
return;
}
//Setting the driver so that we can report results
resultReportingTestWatcher.setDriver(driver);
}

@Test
public void verifySelectingBackPackProduct() throws MalformedURLException {
//Wait for the application to start and load the initial screen (products screen)
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(productsScreenLocator));

// find the Backpack product by image
// Please read about that here: https://github.com/appium/appium/blob/master/packages/images-plugin/docs/find-by-image.md?utm_source=beamer&utm_medium=sidebar&utm_campaign=Now-available-Appium-Image-Selectors-and-Elements-support-on-Real-Device-Cloud&utm_content=ctalink
// And here: https://changelog.saucelabs.com/en/now-available-appium-image-selectors-and-elements-support-on-real-device-cloud-qMPpYR6O
String backpackImage = encoder("src/test/java/com/examples/find_by_image/androidBackpackImage.png");
WebElement backpackImageElement = driver.findElement(AppiumBy.image(backpackImage));

// Start **********************************************************************
// No need to do it. Only if you want to see the image that Appium capture
// The image is saved as an image file "output_image.png"
String returnImage = backpackImageElement.getAttribute("visual");
// Decode Base64 string to image bytes
byte[] imageBytes = Base64.getDecoder().decode(returnImage);

// Convert byte array into BufferedImage
try (ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes)) {
BufferedImage image = ImageIO.read(bis);

// Write the image to a file
File outputFile = new File("src/test/java/com/examples/find_by_image/output_image.png");
ImageIO.write(image, "png", outputFile);
System.out.println("Image has been written successfully");
} catch (IOException e) {
System.err.println("Unable to convert Base64 string to image");
}
// End ************************************************************************

// Click on the image
backpackImageElement.click();

//Verify we are in the backpack product item page
assertThat(isTextAsExpected(productItemTitleLocator, "Backpack", 5)).as("Verify product item title").isTrue();
}

public Boolean isTextAsExpected(By locator, String expectedText ,long timeoutInSeconds) {
try {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(timeoutInSeconds));
wait.until(ExpectedConditions.attributeContains(locator, "text", expectedText ));
} catch (org.openqa.selenium.TimeoutException exception) {
// Report the error to Sauce log
driver.executeScript("sauce:context=Error - The title is not " + expectedText);
return false;
}
return true;
}

public String encoder(String imagePath) {
String base64Image = "";
File file = new File(imagePath);
try (FileInputStream imageInFile = new FileInputStream(file)) {
// Reading a Image file from file system
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
base64Image = Base64.getEncoder().encodeToString(imageData);
} catch (FileNotFoundException e) {
System.out.println("Image not found" + e);
} catch (IOException ioe) {
System.out.println("Exception while reading the Image " + ioe);
}
return base64Image;
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void setup() throws IOException {
//find a device in the cloud
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("appium:automationName", "UiAutomator2");
//Allocate any avilable samsung device with Android version 12
//Allocate any available samsung device with Android version 12
capabilities.setCapability("appium:deviceName", "Samsung.*");
capabilities.setCapability("appium:platformVersion", "12");
String appName = "Android.MyDemoAppRN.apk";
Expand Down Expand Up @@ -115,6 +115,7 @@ public void imageInjectionAndroid() throws InterruptedException {

// inject the image - provide the transformed image to the device with this command
String qrCodeImage = encoder("src/test/java/com/examples/image_injection/images/qr-code.png");

driver.executeScript("sauce:inject-image=" + qrCodeImage);

// Verify that the browser is running
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void setup() throws MalformedURLException {
}

// For all capabilities please check
// http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities
// https://appium.io/docs/en/2.0/guides/caps/
// Use the platform configuration https://saucelabs.com/platform/platform-configurator#/
// to find the emulators/real devices names, OS versions and appium versions you can use for your testings

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,15 @@ public void setUp() throws MalformedURLException {
}

// For all capabilities please check
// http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities
// https://appium.io/docs/en/2.0/guides/caps/
// Use the platform configuration https://saucelabs.com/platform/platform-configurator#/
// to find the simulators/real device names, OS versions and appium versions you can use for your testings

capabilities.setCapability("platformName", "iOS");
capabilities.setCapability("appium:automationName", "XCuiTest");
if (rdc.equals("true")) {
//Allocate any avilable iPhone device with version 14
capabilities.setCapability("appium:deviceName", "iPhone.*");
appName = "SauceLabs-Demo-App.ipa";

sauceOptions.setCapability("resigningEnabled", true);
sauceOptions.setCapability("sauceLabsNetworkCaptureEnabled", true);
}
Expand Down

0 comments on commit 665417f

Please sign in to comment.