Run the sample native app visual test
Step 1: Setup environment
Requirements
- Npm tool
- WebdriverIO framework
- Access key
- You can generate an Access key using the following instruction – Access key
- Billing unit
- The billing unit is your team’s unique identifier, or just the word “personal” in the case of an individual account.
More information can be found here.
- The billing unit is your team’s unique identifier, or just the word “personal” in the case of an individual account.
Step 2: Upload and install an App
Before running tests, several preparations are required:
- Upload the app (only once).
- Find and take a Device
- Install the App
All of these actions can be performed automatically via API.
For more details, please refer to the Upload and Install document.
Step 3: Prepare the test configuration
After setup up the environment, you need to prepare the test configuration
const KEY = 'mobitru_ak....';
const BILLING_UNIT = '<TEAM_CODE>';
const credentials = `${BILLING_UNIT}:${KEY}`;
const encodedCredentials = Buffer.from(credentials).toString('base64');
exports.config = {
runner: 'local',
protocol: 'https',
hostname: `@app.mobitru.com`,
headers: {
Authorization: `Basic ${encodedCredentials}`
},
path: '/wd/hub',
logLevel: 'debug',
port: 443,
specs: ['./test/specs/**/native-appium-demo-ios.js'], // Path to your test file.
maxInstances: 1,
capabilities: [{
platformName: 'iOS',
'appium:automationName': 'XCUITest',
'appium:udid': '00008101-001608482602001E',
'appium:bundleId': 'com.epam.mobitru.demoapp'
}],
bail: 0,
waitForTimeout: 10000, // Default timeout for wait commands
connectionRetryTimeout: 120000,
connectionRetryCount: 3,
framework: 'mocha',
reporters: ['allure'],
mochaOpts: {
ui: 'bdd',
timeout: 60000
}
};Step 4: Add the interaction with visual testing API
After setup up the environment, you need to add the interaction with the visual testing API:
const axios = require('axios');
const FormData = require("form-data");
class VisualTestingAPI {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
// Initialize an Axios instance
this.http = axios.create({
baseURL: this.baseUrl,
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
});
}
async createBuild(inputData) {
const response = await this.http.post("/builds", inputData);
if (response.status === 200) {
return response.data; // VisualBuildData object
} else {
console.log(`Unexpected status code: ${response.status}`);
}
}
async finishBuild(buildId) {
const response = await this.http.post(`/builds/${buildId}/finish`);
if (response.status === 200) {
console.log("Build finished successfully!");
} else {
console.log(`Unexpected status code: ${response.status}`);
}
}
async submitSnapshot(driver, buildId, visualBuildInputData) {
const response = await this.http.post(
`/builds/${buildId}/snapshots`,
visualBuildInputData,
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
'Content-Type': 'multipart/form-data'
},
}
);
if (response.status === 201) {
return response.data; // VisualBuildData object
} else {
console.log(`Unexpected status code: ${response.status}`);
}
}
}Step 5: Run sample test
After setup up the environment, installing the App, preparing the config, and integrating the visual testing API, you can run your first native visual test
const assert = require('assert');
const axios = require('axios');
const fs = require('fs');
const tmp = require('tmp');
const path = require('path');
const FormData = require("form-data");
const API_KEY = 'mobitru_ak....';
const BILLING_UNIT = '<TEAM_CODE>';
const VISUAL_TESTING_API_HOST = 'visual.mobitru.com';
const VISUAL_TESTING_API_BASE_URL = `https://${VISUAL_TESTING_API_HOST}/billing/unit/${BILLING_UNIT}/api/v1`;
const visualTestingApi = new VisualTestingAPI(VISUAL_TESTING_API_BASE_URL, API_KEY);
describe('Appium Login Demo Test', () => {
const VISUAL_TEST_NAME = "visual js demo for ios";
const USER_NAME = 'testuser@mobitru.com';
const PASSWORD = 'password1';
const DEFAULT_WAIT_TIMEOUT_MS = 10000;
let visualBuildData;
before(async () => {
const buildInput = {
name: "Build 1",
project: "JS iOS Native Automation Demo",
branch: "main"
};
visualBuildData = await visualTestingApi.createBuild(buildInput);
console.log("Build Created:", visualBuildData);
});
it('should login to the Mobitru app and verify categories are loaded', async () => {
// Locate the email input field and enter the email
const emailInput = await $('//XCUIElementTypeTextField[starts-with(@name,"Login")]');
await emailInput.setValue(USER_NAME);
await submitSnapshot(driver, 'after_enter_login', VISUAL_TEST_NAME, visualBuildData, visualTestingApi);
// Locate the password input field and enter the password
const passwordInput = await $('//XCUIElementTypeSecureTextField[starts-with(@name,"Password")]');
await passwordInput.setValue(PASSWORD);
await submitSnapshot(driver, 'after_enter_password', VISUAL_TEST_NAME, visualBuildData, visualTestingApi);
// Locate and click the Sign In button
const signInButton = await $('//XCUIElementTypeButton[starts-with(@name,"Sign in")]');
await signInButton.click();
// Wait for the resource category to appear
const productHeader = await $('//*[starts-with(@name,"productHeaderViewLabel")]');
await productHeader.waitForDisplayed({timeout: DEFAULT_WAIT_TIMEOUT_MS});
await submitSnapshot(driver, 'after_login', VISUAL_TEST_NAME, visualBuildData, visualTestingApi);
});
after(async () => {
await visualTestingApi.finishBuild(visualBuildData.id);
});
});
async function submitSnapshot(driver, name, testName, visualBuildData, visualTestingApi) {
//save screenshot
const tmpFile = tmp.fileSync({postfix: '.png'});
const tmpFilePath = tmpFile.name;
await driver.saveScreenshot(tmpFilePath);
const formData = new FormData();
formData.append("name", name);
formData.append("testName", testName);
formData.append("suiteName", visualBuildData.projectName);
formData.append("device", driver.capabilities["appium:udid"]);
formData.append("image", fs.createReadStream(tmpFilePath), {
filename: path.basename(tmpFilePath),
contentType: 'image/png'
});
await visualTestingApi.submitSnapshot(driver, visualBuildData.id, formData);
}