@sap_oss/wdio-qmate-service
Version:
[](https://api.reuse.software/info/github.com/SAP/wdio-qmate-service)[](http
403 lines • 18.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Device = void 0;
// Imports
const verboseLogger_1 = require("../../helper/verboseLogger");
const errorHandler_1 = __importDefault(require("../../helper/errorHandler"));
const ORIENTATION = {
LANDSCAPE: "LANDSCAPE",
PORTRAIT: "PORTRAIT",
UNKNOWN: "UNKNOWN"
};
/**
* @class device
* @memberof mobile
*/
class Device {
vlf = new verboseLogger_1.VerboseLoggerFactory("mobile", "device");
ErrorHandler = new errorHandler_1.default();
//==================================Private Methods===============================================
async isValidPlatform() {
const vl = this.vlf.initLog(this.isValidPlatform);
const SUPPORTED_PLATFORMS = ["android", "ios"];
const platform = await browser.capabilities.platformName;
return SUPPORTED_PLATFORMS.includes(platform.toLowerCase().trim());
}
async executionPlatform() {
const vl = this.vlf.initLog(this.executionPlatform);
const platform = browser.capabilities.platformName;
return platform.toLowerCase().trim();
}
//==================================Public Methods================================================
/**
* @function isAppInstalled
* @memberof mobile.device
* @description Check if the application identified by its Package name/Bundle ID is installed on the device.
* @param {string} appPackageOrBundleId - Android package Name, or iOS bundle Id.
* @returns {boolean} - Returns `true` if specified app package/bundled installed in the device, or `false`.
* @example
* await mobile.device.isAppInstalled("com.google.android.apps.maps");
* await mobile.device.isAppInstalled("com.apple.AppStore")
*/
async isAppInstalled(appPackageOrBundleId) {
const vl = this.vlf.initLog(this.isAppInstalled);
let isAppInstalledInDevice = false;
try {
if (await this.isValidPlatform()) {
isAppInstalledInDevice = browser.isAppInstalled(appPackageOrBundleId);
vl.log(`${await this.executionPlatform()} app installed successfully.`);
}
else {
vl.log(`Unsupported platform while checking the is app installed or not`);
}
}
catch (error) {
this.ErrorHandler.logException(error, "Error: Could not determine if app is installed, The provided package name is invalid.", true);
}
return isAppInstalledInDevice;
}
/**
* @function installApp
* @memberof mobile.device
* @description Install the appropriate app based on the platform the test is being executed on.
* @param {string} appPath - Path of the app(.apk, .ipa)
* @returns {Promise<void>}
* @example
* await mobile.device.installApp("/path/to/your/app.apk");
* await mobile.device.installApp("/path/to/your/app.ipa");
*/
async installApp(appPath) {
const vl = this.vlf.initLog(this.installApp);
try {
vl.log(`Installing ${await this.executionPlatform()} app...`);
if (await this.isValidPlatform()) {
await browser.installApp(appPath);
vl.log(`${await this.executionPlatform()} app installed successfully.`);
}
else {
vl.log(`Unsupported platform ${await this.executionPlatform()} while installing app`);
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Unable to install app. The provided app path ${appPath} does not exist`, true);
}
}
/**
* @function switchToContext
* @memberof mobile.device
* @description Switch to the specified( WEBVIEW | NATIVE_APP ) context if available.
* @param {string} [targetContext='WEBVIEW'] The name of the target context.
* @param {number} [timeout=5000] Maximum time to wait for the web context to appear, milliseconds.
* @returns {Promise<boolean>} Returns `true` if the context is successfully switched, otherwise `false`.
* @example
* await mobile.device.switchToContext();
* await mobile.device.switchToContext("NATIVE_APP", 1000);
*/
async switchToContext(targetContext = "WEBVIEW", timeout = 5000) {
const vl = this.vlf.initLog(this.switchToContext);
try {
let target = this.getTargetContextIfAvailable(targetContext, timeout);
if (target) {
await browser.switchContext(target);
vl.log(`Switched to ${target} context successfully...`);
return true;
}
else {
vl.log(`Switched to ${target} context not successful, it may be null`);
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: No contexts available, Could not switch to ${targetContext} context 'INVALID_CONTEXT'. `, true);
}
return false;
}
/**
* @function getTargetContextIfAvailable
* @memberof mobile.device
* @description
* Returns the specified target context if available within a given timeout.
*
* This method retrieves the list of available contexts and determines if a context
* that matches the `targetContext` string is present. If the target context is found,
* it returns the context name; otherwise, it returns `null`.
*
* @param {string} [targetContext='WEBVIEW'] The name of the target context to check for.
* Common examples are `WEBVIEW` or `NATIVE_APP`.
* @param {number} [timeout=5000] The maximum time, in milliseconds, to wait for the target
* context to become available.
* @returns {Promise<string | null>} The name of the target context if found, or `null` if
* the context is not available within the timeout.
* @example
* const context = await getTargetContextIfAvailable("WEBVIEW", 10000);
* const context = await getTargetContextIfAvailable("NATIVE_APP", 10000);
*/
async getTargetContextIfAvailable(targetContext = "WEBVIEW", timeout = 5000) {
const vl = this.vlf.initLog(this.getTargetContextIfAvailable);
try {
let availableContexts = [];
const isContextAvailable = await browser.waitUntil(async () => {
// Get all available contexts
availableContexts = await browser.getContexts();
return availableContexts.some((context) => context.includes(targetContext));
}, {
timeout,
timeoutMsg: `Target Context "${targetContext}" not found within ${timeout}ms`
});
if (isContextAvailable) {
const target = availableContexts.find((context) => context.includes(targetContext));
vl.log(`Target Context "${target}" is available.`);
return target || null;
}
else {
vl.log(`Target Context ${targetContext} is not available.`);
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: No contexts available, Failed to check is target ${targetContext} context available`, true);
}
return null;
}
/**
* @function closeApplication
* @memberof mobile.device
* @description Close the currently active mobile application.
* @returns {Promise<void>}
* @example
* await mobile.device.closeApplication();
*/
async closeApplication() {
const vl = this.vlf.initLog(this.closeApplication);
try {
await browser.closeApp();
vl.log("The application has been closed successfully.");
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Unable to close app, the app is not currently running`, true);
}
}
/**
* @function queryAppState
* @memberof mobile.device
* @description Queries the state of the application (e.g., running, background, not installed) on the mobile device(Android or iOS).
* @param {string} appPackageOrBundleId - Package name (Android) or bundle ID (iOS) of the application.
* @returns {Promise<number>} - The app state:
* 0 - Not running,
* 1 - Not installed,
* 2 - Running in the background (not suspended),
* 3 - Running in the background (suspended),
* 4 - Running in the foreground.
* @example
* await mobile.device.queryAppState("com.google.android.apps.maps");
* await mobile.device.queryAppState("com.apple.AppStore");
*/
async queryAppState(appPackageOrBundleId) {
const vl = this.vlf.initLog(this.queryAppState);
let appState = -1;
try {
vl.log(`Querying the ${await this.executionPlatform()} app state...`);
if (await this.isValidPlatform()) {
appState = await browser.queryAppState(appPackageOrBundleId);
vl.log(`Application state for ${appPackageOrBundleId} : ${appState}`);
}
else {
vl.log(`Unsupported platform while query app state: ${await this.executionPlatform()}`);
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Unable to query app state, the package name ${appPackageOrBundleId} is invalid or does not exist.`, true);
}
return appState;
}
/**
* @function launchApp
* @memberof mobile.device
* @description Launches the app for both iOS and Android with a parameterized app identifier.
* @param {string} appPackageOrBundleId - The Android package name or iOS bundle ID of the application.
* @returns {Promise<void>} Resolves when the app is successfully launched.
* @example
* await mobile.device.launchApp("com.google.android.apps.maps");
* await mobile.device.launchApp("com.apple.AppStore");
*/
async launchApp(appPackageOrBundleId) {
const vl = this.vlf.initLog(this.launchApp);
try {
vl.log(`Launching ${await this.executionPlatform()} app...`);
if (await this.isValidPlatform()) {
await browser.activateApp(appPackageOrBundleId);
vl.log(`${await this.executionPlatform()} App launched successfully with given ${appPackageOrBundleId} Package/bundle ID`);
}
else {
vl.log(`Unsupported platform while launching the app: ${await this.executionPlatform()}`);
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Unable to launch the app, the package name ${appPackageOrBundleId} is invalid or does not exist.`, true);
}
}
/**
* @function switchToLandscapeOrientation
* @memberof mobile.device
* @description Switches the device orientation to landscape mode.
* @returns {Promise<void>} Resolves when the orientation is successfully switched.
* @example
* await mobile.device.switchToLandscapeOrientation();
*/
async switchToLandscapeOrientation() {
const vl = this.vlf.initLog(this.switchToLandscapeOrientation);
try {
const currentOrientation = await browser.getOrientation();
if (currentOrientation === ORIENTATION.LANDSCAPE) {
vl.log("Device is already in landscape mode.");
return;
}
vl.log("Switching device orientation to landscape...");
await browser.setOrientation(ORIENTATION.LANDSCAPE);
vl.log("Device orientation successfully switched to landscape.");
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Could not change device orientation, Invalid argument: The orientation must be 'LANDSCAPE'.`, true);
}
}
/**
* @function switchToPortraitOrientation
* @memberof mobile.device
* @description Switches the device orientation to portrait mode.
* @returns {Promise<void>} Resolves when the orientation is successfully switched.
* @example
* await mobile.device.switchToPortraitOrientation();
*/
async switchToPortraitOrientation() {
const vl = this.vlf.initLog(this.switchToPortraitOrientation);
try {
const currentOrientation = await browser.getOrientation();
if (currentOrientation === ORIENTATION.PORTRAIT) {
vl.log("Device is already in portrait mode.");
return;
}
vl.log("Switching device orientation to portrait...");
await browser.setOrientation(ORIENTATION.PORTRAIT);
vl.log("Device orientation successfully switched to portrait.");
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Could not change device orientation, Invalid argument: The orientation must be 'PORTRAIT`, true);
}
}
/**
* @function getCurrentOrientation
* @memberof mobile.device
* @description Returns the device current orientation (PORTRAIT or LANDSCAPE)
* @returns {Promise<Orientation>} The current device orientation.
* @example
* await mobile.device.getCurrentOrientation();
*/
async getCurrentOrientation() {
const vl = this.vlf.initLog(this.getCurrentOrientation);
let orientation = ORIENTATION.UNKNOWN; // Default value
try {
orientation = await browser.getOrientation();
vl.log(`Current device orientation: ${orientation}`);
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Could not get the current device orientation`, true);
}
return orientation;
}
/**
* @function hideKeyboard
* @memberof mobile.device
* @description Hides the keyboard on both Android and iOS using specific strategies with timeout.
* @param {string} strategy - Strategy to use for hiding the keyboard ('pressKey', 'tapOutside', 'swipeDown').
* @param {string} key - Key to press if using the 'pressKey' strategy (e.g., 'Done', 'Enter').
* @param {number} keyCode - Key code for Android (optional).
* @param {number} [timeout=5000] - Timeout in milliseconds for retrying to hide the keyboard.
* @returns {Promise<void>}
* @example
* await mobile.device.hideKeyboard();
* await mobile.device.hideKeyboard('tapOutside');
* await mobile.device.hideKeyboard('swipeDown');
* //Android only, Sends a specific key code, like 66 for "Enter."
* await mobile.device.hideKeyboard('pressKey', undefined, 66);
* await mobile.device.hideKeyboard('pressKey', 'Done');
*/
async hideKeyboard(strategy, key, keyCode, timeout = 5000) {
const vl = this.vlf.initLog(this.hideKeyboard);
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
try {
if (await util.browser.isAndroid()) {
vl.log("Hiding keyboard on Android.");
await browser.hideKeyboard(strategy, key, keyCode);
}
else if (await util.browser.isIos()) {
vl.log("Hiding keyboard on iOS.");
await browser.execute("mobile: hideKeyboard", { strategy });
}
else {
vl.log("Unsupported platform: Unable to hide the keyboard.");
return;
}
vl.log("Keyboard hidden successfully.");
return; // Exit if the keyboard is successfully hidden
}
catch (error) {
// Wait briefly before retrying
await new Promise((resolve) => setTimeout(resolve, 500));
this.ErrorHandler.logException(error, `Error: Failed to hide the keyboard, Retrying...`, true);
}
}
vl.log(`Failed to hide the keyboard within the timeout of ${timeout}ms.`);
}
/**
* @function isKeyboardVisible
* @memberof mobile.device
* @description Checks if the keyboard is visible or not on the mobile device.
* @returns {Promise<boolean>} Returns `true` if the keyboard is visible on the mobile view.
* @example
* await mobile.device.isKeyboardVisible();
*/
async isKeyboardVisible() {
const vl = this.vlf.initLog(this.isKeyboardVisible);
let isKeyboardVisible = false;
try {
if (await util.browser.isAndroid()) {
vl.log("check if the screen height is reduced due to the keyboard");
// For Android, check if the screen height is reduced due to the keyboard
const windowRect = await browser.getWindowRect();
const screenSize = await browser.getWindowSize();
// If the visible height is less, keyboard is visible
vl.log("if visible height is less, keyboard is visible");
isKeyboardVisible = windowRect.height < screenSize.height;
}
else if (await util.browser.isIos()) {
// For iOS, check if the keyboard is displayed via Appium's mobile API
const isKeyboardShown = await browser.execute("mobile: isKeyboardShown");
isKeyboardVisible = isKeyboardShown === true; // Returns true if the keyboard is visible
}
else {
vl.log("Unsupported platform: Unable to detect keyboard visibility.");
return false;
}
}
catch (error) {
this.ErrorHandler.logException(error, `Error: Failed to get the is keyboard visible`, true);
}
return isKeyboardVisible;
}
/**
* @function isPlatformSupported
* @memberof mobile.device
* @description Determine if the current platform is supported, if the current device platform is either `Android` or `iOS`.
* @returns {Promise<boolean>} If neither Android nor iOS is detected (e.g., Windows, Linux, or web), the condition evaluates to false
* @example
* await mobile.device.isPlatformSupported();
*/
async isPlatformSupported() {
return (await util.browser.isAndroid()) || (await util.browser.isIos());
}
}
exports.Device = Device;
exports.default = new Device();
//# sourceMappingURL=device.js.map