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.

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);
}

Scroll to Top