Run tests with QR code scan
Below you can find information on how to run tests, which include QR code scan
Prerequisites
- Existing native application with a feature, related to the QR code scan.
- The app is installed on a device with image injection enabled.
More details can be found here and in our API reference collection. - The tests can operate with system alerts
For example, using the Appium command. - Access key is generated.
Step 1: Add executing of the QR code upload API
We have a specific API action for uploading QR codes in our public API.
The action accepts a QR code as the file, which should be sent in the “multipart/form-data” parameter.
Below you can find an example for popular programming languages.
// Rest-assured library is using here https://rest-assured.io/
public void uploadQrCode(String deviceSerial, File qrCode) {
RestAssured.given().auth().
oauth2(<ACCESS_KEY>).
baseUri("https://app.mobitru.com/billing/unit/"+
<TEAM_ACCOUNT_NAME>
+"/automation/api").
header("X-Content-Type", "image/png").
header("x-File-Name", qrCode.getName()).
contentType("multipart/form-data").
multiPart("file", qrCode).
post("device/{serial}/injection/image", deviceSerial).then().
statusCode(HTTP_NO_CONTENT);
}
const axios = require('axios');
const fs = require('fs');
const path = require('path')
const FormData = require('form-data');
const KEY = 'mobitru_ak_....';
const PROJECT_NAME = '<TEAM_CODE>';
const APPIUM_HUB = 'app.mobitru.com';
async function uploadQrCode(qrCodePath) {
// Create FormData
const formData = new FormData();
formData.append('file', fs.createReadStream(qrCodePath));
// Config for headers
const headers = {
// Add multipart headers specific to the FormData instance
...formData.getHeaders(),
"Authorization": `Bearer ${KEY}`,
"X-Content-Type": "image/png",
"x-File-Name": qrCodePath.split('/').pop() // Extract file name
};
// extract device serial from capabilities and build an API url
const serial = browser.options.capabilities["appium:udid"];
const apiUrl = `https://${APPIUM_HUB}/billing/unit/${PROJECT_NAME}/automation/api`;
const url = `${apiUrl}/device/${serial}/injection/image`;
// Send POST request
const response = await axios.post(url, formData, {headers});
// Check for status code
if (response.status === 204) {
console.log("File uploaded successfully!");
} else {
console.error(`Failed to upload file. Status code: ${response.status}, Message: ${response.statusText}`);
}
}
import requests
PROJECT_NAME = "<TEAM_CODE>"
ACCESS_KEY = "mobitru_ak_...."
APPIUM_HUB = "app.mobitru.com"
DEVICE_UDID = "<DEVICE_SERIAL>"
def upload_qr_code(qr_code_path):
api_url = f"https://{APPIUM_HUB}/billing/unit/{PROJECT_NAME}/automation/api"
headers = {
"Authorization": f"Bearer {ACCESS_KEY}",
"X-Content-Type": "image/png",
"x-File-Name": qr_code_path.split("/")[-1] # extract the file name
}
files = {
"file": open(qr_code_path, "rb")
}
url = f"{api_url}/device/{DEVICE_UDID}/injection/image"
response = requests.post(
url,
headers=headers,
files=files
)
if response.status_code == 204: # HTTP_NO_CONTENT
print("QR code uploaded successfully!")
else:
print(f"Failed to upload QR code. Status code: {response.status_code}, Response: {response.text}")
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
....
const string ProjectName = "<TEAM_CODE>";
const string AccessKey = "mobitru_ak_....";
const string AppiumHost = "app.mobitru.com";
const string DeviceUdid = "00008101-001608482602001E";
private void UploadQrCode(string qrCodePath)
{
// API URL
string apiUrl = $"https://{AppiumHost}/billing/unit/{ProjectName}/automation/api/device/{DeviceUdid}/injection/image";
using (var httpClient = new HttpClient())
{
// Headers
var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessKey);
request.Headers.Add("X-Content-Type", "image/png");
request.Headers.Add("x-File-Name", Path.GetFileName(qrCodePath)); // Extract file name
// File to upload
var formData = new MultipartFormDataContent();
using (var fileStream = new FileStream(qrCodePath, FileMode.Open, FileAccess.Read))
{
var fileContent = new StreamContent(fileStream);
fileContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
formData.Add(fileContent, "file", Path.GetFileName(qrCodePath));
request.Content = formData;
// Send POST request (synchronous)
var response = httpClient.Send(request);
// Handle response
if (response.IsSuccessStatusCode && response.StatusCode == System.Net.HttpStatusCode.NoContent)
{
Console.WriteLine("QR code uploaded successfully!");
}
else
{
Console.WriteLine($"Failed to upload QR code. Status code: {(int)response.StatusCode}, Response: {response.Content.ReadAsStringAsync().Result}");
}
}
}
}
Step 2: Cover the QR code scan pop-up
It depends on the Platform, but usually, the two kinds of behavior should be covered:
- For Android: “allow permission” action on the Camera dialog.
- For iOS: “accept” action on a simple system Alert.
Both pop-ups typically activate when the QR code scanning process is initiated.
Also, here is an example of a single method for handling both items using Appium:
@AndroidFindBy(xpath = "//*[@resource-id='com.android.permissioncontroller:id/permission_allow_one_time_button']")
private WebElement allowPermissionsOnceButton;
public void openUploadQrCodeScreen() {
//example of trigger the QR code scan process
applyPromoButton.click();
//wait for the process is started
new WebDriverWait(driver, Duration.ofSeconds(10), Duration.ofSeconds(1))
.ignoring(WebDriverException.class)
.until(ExpectedConditions.invisibilityOf(applyPromoButton));
//make different actions depending on the platform
if (driver.getCapabilities().getPlatformName().equals(Platform.ANDROID)){
// wait for allow permission button and click on it
waitForElementDisplayed(allowPermissionsOnceButton);
allowPermissionsOnceButton.click();
}else{
// wait for simple system alert and perform accept
new WebDriverWait(driver, Duration.ofSeconds(10), Duration.ofSeconds(1))
.ignoring(WebDriverException.class)
.until(ExpectedConditions.alertIsPresent());
driver.switchTo().alert().accept();
}
}
// The example is for WebdriverIO framework
async function openQRCodeScreen() {
// Activate uploading a QR code
const activateXPath = browser.isAndroid
? "//*[@resource-id='com.epam.mobitru:id/apply_promo_code']" // Android XPath
: "//XCUIElementTypeButton[@name='Apply promo code']"; // iOS XPath
const activateElement = await driver.$(activateXPath);
await activateElement.click();
// Handle camera permissions
if (browser.isAndroid) {
const allowButtonXPath = "//*[@resource-id='com.android.permissioncontroller:id/permission_allow_one_time_button']";
const allowButton = await driver.$(allowButtonXPath);
await allowButton.click();
} else {
// Wait for the iOS alert and accept it
await driver.waitUntil(
async () => (await driver.getAlertText()) !== null,
{timeout: DEFAULT_WAIT_TIMEOUT_MS, timeoutMsg: "Alert not found"}
);
await browser.acceptAlert();
}
}
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def open_qr_code_screen(driver):
is_android = "android" == driver.capabilities.get("platformName").lower()
# activate uploading a qr code
activate_xpath = "//*[@resource-id='com.epam.mobitru:id/apply_promo_code']" if is_android else "//XCUIElementTypeButton[@name='Apply promo code']"
driver.find_element(AppiumBy.XPATH,activate_xpath).click()
# allow camera permissions
if is_android:
driver.find_element(AppiumBy.XPATH,
"//*[@resource-id='com.android.permissioncontroller:id/permission_allow_one_time_button']").click()
else:
WebDriverWait(driver, DEFAULT_WAIT_TIMEOUT_SEC).until(
EC.alert_is_present()
)
driver.switch_to.alert.accept()
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.iOS;
using OpenQA.Selenium.Support.UI;
private void OpenQrCodeScreen()
{
bool isAndroid = driver.PlatformName.ToLower().Equals("android");
// XPath for element activation
string activateXPath = isAndroid
? "//*[@resource-id='com.epam.mobitru:id/apply_promo_code']"
: "//XCUIElementTypeButton[@name='Apply promo code']";
// Find and click the element
AppiumElement activateElement = driver.FindElement(By.XPath(activateXPath));
activateElement.Click();
if (isAndroid)
{
// Click the allow button on Android
string allowButtonXPath = "//*[@resource-id='com.android.permissioncontroller:id/permission_allow_one_time_button']";
IWebElement allowButton = driver.FindElement(By.XPath(allowButtonXPath));
allowButton.Click();
}
else
{
// Wait for the iOS alert to appear and then accept it
WebDriverWait wait = new(driver, TimeSpan.FromSeconds(DefaultWaitTimeoutSec));
wait.Until(driver => !string.IsNullOrEmpty(driver.SwitchTo().Alert().Text));
var alert = driver.SwitchTo().Alert();
alert.Accept();
}
}
Step 3: Use both actions in a test
In the end, you can cover the full scenario by using both actions one after another.
Below you can find an example for popular programming languages:
@Test(description = "Check apply promo from qr code")
public void checkPromoApply() {
//read a QR code image to file object
File qrCode = new File(IOUtils.resourceToURL("qr.png", this.getClass().getClassLoader()).getFile());
//trigger the QR code scan process
openUploadQrCodeScreen();
//upload the QR code image, which will be applied automatically
uploadQrCode(udid, qrCode);
//actions after apply the QR code...
}
// The example is for WebdriverIO framework
describe('Appium QR Code Test', () => {
it('should upload a QR code successfully', async () => {
// trigger the QR code scan process
await openQRCodeScreen(driver);
//perform the qr code uploading
await uploadQrCode(path.resolve(__dirname, "qr.png"))
//wait for the element, which appears after success uploading
const resultXpath = browser.isAndroid
? "//*[@resource-id='com.epam.mobitru:id/applied']" // Android XPath
: "//*[contains(@name,'applied')]"; // iOS XPath
const resultElement = await $(resultXpath);
await resultElement.waitForDisplayed({timeout: DEFAULT_WAIT_TIMEOUT_MS});
});
});
def test_qr_code_apply(driver):
is_android = "android" == driver.capabilities.get("platformName").lower()
# trigger the QR code scan process
open_qr_code_screen(driver)
# perform the qr code uploading
upload_qr_code("qr.png")
# wait for the element, which appears after success uploading
result_xpath = "//*[@resource-id='com.epam.mobitru:id/applied']" if is_android else "//*[contains(@name,'applied')]"
WebDriverWait(driver, DEFAULT_WAIT_TIMEOUT_SEC).until(
EC.visibility_of_element_located((MobileBy.XPATH, result_xpath))
)
using NUnit.Framework;
[Test]
public void TestQrCode()
{
// trigger the QR code scan process
OpenQrCodeScreen();
//perform the qr code uploading
UploadQrCode(Path.Combine(Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName, "qr.png"));
// wait for element, which appears after the QR code upload
// Select the correct XPath based on the platform
string resultXPath = driver.PlatformName.ToLower().Equals("android")
? "//*[@resource-id='com.epam.mobitru:id/applied']"
: "//*[contains(@name,'applied')]";
// Wait for the element using WebDriverWait
WebDriverWait wait = new(driver, TimeSpan.FromSeconds(DefaultWaitTimeoutSec));
wait.Until(driver => driver.FindElement(By.XPath(resultXPath)).Displayed);
}
Note:
If you don’t have an Application with the QR code scan but would like to try the described steps, please use our open-source demo apps for Android and iOS.