-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Teodor Nikolov
authored and
Teodor Nikolov
committed
Apr 3, 2024
0 parents
commit 1bf5f69
Showing
150 changed files
with
23,621 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
node_modules | ||
lib | ||
tet-results | ||
npm-debug.log | ||
.nyc | ||
.env | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "@bellatrix/appium", | ||
"version": "0.0.1", | ||
"type": "module", | ||
"exports": { | ||
"./core": "./src/core/index.ts", | ||
"./android": "./src/android/index.ts", | ||
"./ios": "./src/ios/index.ts", | ||
"./windows": "./src/windows/index.ts", | ||
"./common/commands": "./src/common/commands/index.ts", | ||
"./core/contracts": "./src/core/contracts/index.ts", | ||
"./common/http": "./src/common/http/index.ts", | ||
"./types": "./src/types/index.ts", | ||
"./common/http/contracts": "./src/common/http/contracts/index.ts" | ||
}, | ||
"devDependencies": { | ||
"@types/selenium-webdriver": "^4.1.21" | ||
}, | ||
"dependencies": { | ||
"@bellatrix/core": "file:../core", | ||
"selenium-webdriver": "^4.16.0" | ||
}, | ||
"peerDependencies": { | ||
"typescript": "^5.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { AndroidDriver } from "@bellatrix/appium/android"; | ||
|
||
const capabilities = { | ||
platformName: 'Android', | ||
'appium:avd': 'android34', | ||
'appium:app': '/Users/Teodor/Downloads/ApiDemos-debug.apk', | ||
'appium:noReset': false, | ||
'appium:automationName': 'UiAutomator2' | ||
}; | ||
|
||
const driver = new AndroidDriver('http://127.0.0.1:4723/', capabilities); | ||
|
||
async function myTest() { | ||
await driver.createSession(); | ||
await driver.getBatteryInfo().then(console.log); | ||
await driver.deleteSession(); | ||
} | ||
|
||
myTest(); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { WindowsDriver } from "./windows/WindowsDriver"; | ||
|
||
const capabilities = { | ||
platformName: 'Windows', | ||
'appium:automationName': 'Windows', | ||
'appium:app': 'Root', | ||
}; | ||
|
||
const driver = new WindowsDriver('http://172.27.80.1:4723/', capabilities); | ||
|
||
async function myTest() { | ||
await driver.createSession(); | ||
await driver.deleteSession(); | ||
} | ||
|
||
myTest(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import { Image } from '@bellatrix/core/image'; | ||
import { AndroidPermission } from '@bellatrix/appium/types'; | ||
import { AppiumDriver } from '@bellatrix/appium/core'; | ||
import { MobileCommands } from '@bellatrix/appium/common/commands'; | ||
|
||
|
||
type CreatedSessionResponse = { | ||
capabilities: object; | ||
sessionId: string; | ||
} | ||
|
||
type AndroidBatteryInfo = { | ||
percentage: number; | ||
state: BatteryState; | ||
} | ||
|
||
type BatteryState = 'Unknown' | 'Charging' | 'Discharging' | 'Not charging' | 'Full'; | ||
|
||
type EditorAction = 'normal' | 'unspecified' | 'none' | 'go' | 'search' | 'send' | 'next' | 'done' | 'previous'; | ||
|
||
type PermissionType = 'denied' | 'granted' | 'requested'; | ||
|
||
type SMSListResponse = { | ||
items: SMS[]; | ||
total: number; | ||
} | ||
|
||
type SMS = { | ||
id: string; | ||
address: string; | ||
person: string | null; | ||
date: string; | ||
read: string; | ||
status: string; | ||
type: string; | ||
subject: string | null; | ||
body: string; | ||
serviceCenter: string | null; | ||
} | ||
|
||
export class AndroidDriver extends AppiumDriver { | ||
private static readonly ANDROID_PLATOFRM = 'Android'; | ||
|
||
constructor(serverUrl: string, capabilities: object) { | ||
super(serverUrl, AppiumDriver.validatePlatformName(capabilities, AndroidDriver.ANDROID_PLATOFRM)); | ||
} | ||
|
||
async createSession(): Promise<void> { | ||
const response = await this.commandExecutor.execute<CreatedSessionResponse>(MobileCommands.CREATE_SESSION, { capabilities: { alwaysMatch: this.capabilities } }); | ||
this.commandExecutor.setSessionId(response.sessionId); | ||
} | ||
|
||
async deleteSession(): Promise<void> { | ||
const response = await this.commandExecutor.execute(MobileCommands.DELETE_SESSION) | ||
this.commandExecutor.unsetSessionId(); | ||
} | ||
|
||
async getContexts(): Promise<string[]> { | ||
return this.commandExecutor.execute(MobileCommands.GET_CONTEXTS); | ||
} | ||
|
||
async getCurrentContext(): Promise<string> { | ||
return this.commandExecutor.execute(MobileCommands.GET_CURRENT_CONTEXT); | ||
} | ||
|
||
async setContext(name: string): Promise<void> { | ||
this.commandExecutor.execute(MobileCommands.SET_CONTEXT, { name }); | ||
} | ||
|
||
async openNotifications(): Promise<void> { | ||
this.commandExecutor.execute(MobileCommands.OPEN_NOTIFICATIONS); | ||
} | ||
|
||
async closeNotifications(): Promise<void> { | ||
this.executeADBShellCommand('service call statusbar 2'); | ||
} | ||
|
||
async toggleLocationServices(): Promise<void> { | ||
this.commandExecutor.execute(MobileCommands.TOGGLE_LOCATION_SERVICES); | ||
} | ||
|
||
async getBatteryInfo(): Promise<AndroidBatteryInfo> { | ||
const response: { level: number; state: number } = await this.execute('mobile: batteryInfo'); | ||
const percentage = response.level * 100; | ||
|
||
const stateMap: Record<number, BatteryState> = { | ||
2: 'Charging', | ||
3: 'Discharging', | ||
4: 'Not charging', | ||
5: 'Full', | ||
}; | ||
|
||
const state: BatteryState = stateMap[response.state] || 'Unknown'; | ||
|
||
return { percentage, state } | ||
} | ||
|
||
async execute<T>(script: string, args: object = {}): Promise<T> { | ||
return this.commandExecutor.execute(MobileCommands.EXECUTE_SCRIPT, { script, args }) as T; | ||
} | ||
|
||
async executeADBShellCommand<T>(command: string, includeStderr?: boolean, timeout?: number): Promise<T>; | ||
async executeADBShellCommand<T>(command: string, timeout?: number): Promise<T>; | ||
async executeADBShellCommand<T>(command: string, includeStderrOrTimeout?: boolean | number, timeout?: number): Promise<T> { | ||
let includeStderr: boolean | undefined; | ||
if (typeof includeStderrOrTimeout === 'boolean') { | ||
includeStderr = includeStderrOrTimeout; | ||
} else { | ||
timeout = includeStderrOrTimeout; | ||
} | ||
|
||
const tokens = command.split(/\s+/); | ||
return this.execute('mobile: shell', { command: tokens[0], args: tokens.slice(1), includeStderr, timeout }); | ||
} | ||
|
||
async performEditorAction(action: EditorAction) { | ||
return this.execute('mobile: performEditorAction', { action }) | ||
} | ||
|
||
async getPermissions(type?: PermissionType, appPackage?: string): Promise<AndroidPermission[]> { | ||
return (await this.execute<string[]>('mobile: getPermissions', { type, appPackage })) | ||
.map(permission => permission.replace('android.permission.', '')) as AndroidPermission[] | ||
} | ||
|
||
async grantPermissions(...permissions: AndroidPermission[]): Promise<void> { | ||
return this.execute('mobile: changePermissions', { | ||
action: 'grant', permissions: permissions | ||
.map(permission => `android.permission.${permission}`) | ||
}) | ||
} | ||
|
||
async revokePermission(...permissions: AndroidPermission[]): Promise<void> { | ||
return this.execute('mobile: changePermissions', { | ||
action: 'revoke', permissions: permissions | ||
.map(permission => `android.permission.${permission}`) | ||
}) | ||
} | ||
|
||
async getScreenshot(): Promise<Image> { | ||
const base64image = await this.commandExecutor.execute<Promise<string>>(MobileCommands.SCREENSHOT); | ||
return Image.fromBase64(base64image); | ||
} | ||
|
||
async getElementScreenshot(elementId: string) { // WIP:temp | ||
this.commandExecutor.setParam('elementId', elementId); | ||
const base64image = await this.commandExecutor.execute<Promise<string>>(MobileCommands.ELEMENT_SCREENSHOT); | ||
return base64image; | ||
} | ||
|
||
async getActiveElement() { // WIP | ||
return this.commandExecutor.execute(MobileCommands.GET_ACTIVE_ELEMENT); | ||
} | ||
|
||
async listSMS(): Promise<SMSListResponse> { | ||
return this.execute('mobile: listSms'); | ||
} | ||
|
||
async startRecordingScreen(): Promise<void> { | ||
return this.commandExecutor.execute(MobileCommands.START_RECORDING_SCREEN); | ||
} | ||
|
||
async stopRecordingScreen() { // WIP | ||
const base64video = this.commandExecutor.execute(MobileCommands.STOP_RECORDING_SCREEN); | ||
return base64video; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './AndroidDriver'; |
148 changes: 148 additions & 0 deletions
148
@bellatrix/appium/src/common/commands/MobileCommands.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { CommandInfo } from '@bellatrix/appium/types'; | ||
import { CommandRepository } from '@bellatrix/appium/core/contracts'; | ||
|
||
export class MobileCommands { | ||
static readonly CREATE_SESSION = 'createSession'; | ||
static readonly DELETE_SESSION = 'deleteSession'; | ||
static readonly GET_CONTEXTS = 'getContexts'; | ||
static readonly GET_CURRENT_CONTEXT = 'getCurrentContext'; | ||
static readonly SET_CONTEXT = 'setContext'; | ||
static readonly EXECUTE_SCRIPT = 'executeScript'; | ||
static readonly IMPLICIT_WAIT = 'implicitWait'; | ||
static readonly AVAILABLE_IME_ENGINES = 'availableIMEEngines'; | ||
static readonly ACTIVE_IME_ENGINE = 'getActiveIMEEngine'; | ||
static readonly IS_IME_ACTIVATED = 'isIMEActivated'; | ||
static readonly DEACTIVATE_IME_ENGINE = 'deactivateIMEEngine'; | ||
static readonly ACTIVATE_IME_ENGINE = 'activateIMEEngine'; | ||
static readonly GET_WINDOW_SIZE = 'getWindowSize'; | ||
static readonly KEYS = 'keys'; | ||
static readonly GET_LOCATION = 'getLocation'; | ||
static readonly GET_LOCATION_IN_VIEW = 'getLocationInView'; | ||
static readonly GET_SIZE = 'getSize'; | ||
static readonly TOUCH_CLICK = 'click'; | ||
static readonly TOUCH_DOWN = 'touchDown'; | ||
static readonly TOUCH_UP = 'touchUp'; | ||
static readonly TOUCH_MOVE = 'touchMove'; | ||
static readonly TOUCH_LONG_CLICK = 'touchLongClick'; | ||
static readonly TOUCH_FLICK = 'flick'; | ||
static readonly TOUCH_PERFORM = 'performTouch'; | ||
static readonly TOUCH_MULTI_PERFORM = 'performMultiAction'; | ||
static readonly DEVICE_LOCK = 'lock'; | ||
static readonly DEVICE_UNLOCK = 'unlock'; | ||
static readonly IS_DEVICE_LOCKED = 'isLocked'; | ||
static readonly START_RECORDING_SCREEN = 'startRecordingScreen'; | ||
static readonly STOP_RECORDING_SCREEN = 'stopRecordingScreen'; | ||
static readonly GET_PERFORMANCE_DATA_TYPES = 'getPerformanceDataTypes'; | ||
static readonly GET_PERFORMANCE_DATA = 'getPerformanceData'; | ||
static readonly PRESS_KEYCODE = 'pressKeyCode'; | ||
static readonly LONG_PRESS_KEYCODE = 'longPressKeyCode'; | ||
static readonly FINGERPRINT = 'fingerprint'; | ||
static readonly SEND_SMS = 'sendSMS'; | ||
static readonly GSM_CALL = 'gsmCall'; | ||
static readonly GSM_SIGNAL = 'gsmSignal'; | ||
static readonly GSM_VOICE = 'gsmVoice'; | ||
static readonly POWER_CAPACITY = 'powerCapacity'; | ||
static readonly POWER_AC = 'powerAC'; | ||
static readonly NETWORK_SPEED = 'networkSpeed'; | ||
static readonly KEYEVENT = 'keyevent'; | ||
static readonly GET_CURRENT_ACTIVITY = 'getCurrentActivity'; | ||
static readonly GET_CURRENT_PACKAGE = 'getCurrentPackage'; | ||
static readonly QUERY_APP_STATE = 'queryAppState'; | ||
static readonly TOGGLE_AIRPLANE_MODE = 'toggleFlightMode'; | ||
static readonly TOGGLE_DATA = 'toggleData'; | ||
static readonly TOGGLE_WIFI = 'toggleWiFi'; | ||
static readonly TOGGLE_LOCATION_SERVICES = 'toggleLocationServices'; | ||
static readonly OPEN_NOTIFICATIONS = 'openNotifications'; | ||
static readonly START_ACTIVITY = 'startActivity'; | ||
static readonly GET_SYSTEM_BARS = 'getSystemBars'; | ||
static readonly GET_DISPLAY_DENSITY = 'getDisplayDensity'; | ||
static readonly LAUNCH_APP = 'launchApp'; | ||
static readonly CLOSE_APP = 'closeApp'; | ||
static readonly RESET_APP = 'reset'; | ||
static readonly BACKGROUND_APP = 'background'; | ||
static readonly GET_STRINGS = 'getStrings'; | ||
static readonly SET_VALUE_IMMEDIATE = 'setValueImmediate'; | ||
static readonly REPLACE_VALUE = 'replaceValue'; | ||
static readonly SCREENSHOT = 'screenshot'; | ||
static readonly ELEMENT_SCREENSHOT = 'elementScreenshot' | ||
static readonly GET_ACTIVE_ELEMENT = 'getActiveElement' | ||
|
||
public static commandRepository: CommandRepository = new Map([ | ||
[this.CREATE_SESSION, this.postCommand('/session')], | ||
[this.DELETE_SESSION, this.deleteCommand('/session/:sessionId')], | ||
[this.GET_CONTEXTS, this.getCommand('/session/:sessionId/contexts')], | ||
[this.GET_CURRENT_CONTEXT, this.getCommand('/session/:sessionId/context')], | ||
[this.SET_CONTEXT, this.postCommand('/session/:sessionId/context')], | ||
[this.EXECUTE_SCRIPT, this.postCommand('/session/:sessionId/execute')], | ||
[this.IMPLICIT_WAIT, this.postCommand('/session/:sessionId/timeouts/implicit_wait')], | ||
[this.AVAILABLE_IME_ENGINES, this.getCommand('/session/:sessionId/ime/available_engines')], | ||
[this.ACTIVE_IME_ENGINE, this.getCommand('/session/:sessionId/ime/active_engine')], | ||
[this.IS_IME_ACTIVATED, this.getCommand('/session/:sessionId/ime/activated')], | ||
[this.DEACTIVATE_IME_ENGINE, this.postCommand('/session/:sessionId/ime/deactivate')], | ||
[this.ACTIVATE_IME_ENGINE, this.postCommand('/session/:sessionId/ime/activate')], | ||
[this.GET_WINDOW_SIZE, this.getCommand('/session/:sessionId/window/:windowhandle/size')], | ||
[this.KEYS, this.postCommand('/session/:sessionId/keys')], | ||
[this.GET_LOCATION, this.getCommand('/session/:sessionId/element/:elementId/location')], | ||
[this.GET_LOCATION_IN_VIEW, this.getCommand('/session/:sessionId/element/:elementId/location_in_view')], | ||
[this.GET_SIZE, this.getCommand('/session/:sessionId/element/:elementId/size')], | ||
[this.TOUCH_CLICK, this.postCommand('/session/:sessionId/touch/click')], | ||
[this.TOUCH_DOWN, this.postCommand('/session/:sessionId/touch/down')], | ||
[this.TOUCH_UP, this.postCommand('/session/:sessionId/touch/up')], | ||
[this.TOUCH_MOVE, this.postCommand('/session/:sessionId/touch/move')], | ||
[this.TOUCH_LONG_CLICK, this.postCommand('/session/:sessionId/touch/longclick')], | ||
[this.TOUCH_FLICK, this.postCommand('/session/:sessionId/touch/flick')], | ||
[this.TOUCH_PERFORM, this.postCommand('/session/:sessionId/touch/perform')], | ||
[this.TOUCH_MULTI_PERFORM, this.postCommand('/session/:sessionId/touch/multi/perform')], | ||
[this.DEVICE_LOCK, this.postCommand('/session/:sessionId/appium/device/lock')], | ||
[this.DEVICE_UNLOCK, this.postCommand('/session/:sessionId/appium/device/unlock')], | ||
[this.IS_DEVICE_LOCKED, this.postCommand('/session/:sessionId/appium/device/is_locked')], | ||
[this.START_RECORDING_SCREEN, this.postCommand('/session/:sessionId/appium/start_recording_screen')], | ||
[this.STOP_RECORDING_SCREEN, this.postCommand('/session/:sessionId/appium/stop_recording_screen')], | ||
[this.GET_PERFORMANCE_DATA_TYPES, this.postCommand('/session/:sessionId/appium/performanceData/types')], | ||
[this.GET_PERFORMANCE_DATA, this.postCommand('/session/:sessionId/appium/getPerformanceData')], | ||
[this.PRESS_KEYCODE, this.postCommand('/session/:sessionId/appium/device/press_keycode')], | ||
[this.LONG_PRESS_KEYCODE, this.postCommand('/session/:sessionId/appium/device/long_press_keycode')], | ||
[this.FINGERPRINT, this.postCommand('/session/:sessionId/appium/device/finger_print')], | ||
[this.SEND_SMS, this.postCommand('/session/:sessionId/appium/device/send_sms')], | ||
[this.GSM_CALL, this.postCommand('/session/:sessionId/appium/device/gsm_call')], | ||
[this.GSM_SIGNAL, this.postCommand('/session/:sessionId/appium/device/gsm_signal')], | ||
[this.GSM_VOICE, this.postCommand('/session/:sessionId/appium/device/gsm_voice')], | ||
[this.POWER_CAPACITY, this.postCommand('/session/:sessionId/appium/device/power_capacity')], | ||
[this.POWER_AC, this.postCommand('/session/:sessionId/appium/device/power_ac')], | ||
[this.NETWORK_SPEED, this.postCommand('/session/:sessionId/appium/device/network_speed')], | ||
[this.KEYEVENT, this.postCommand('/session/:sessionId/appium/device/keyevent')], | ||
[this.GET_CURRENT_ACTIVITY, this.getCommand('/session/:sessionId/appium/device/current_activity')], | ||
[this.GET_CURRENT_PACKAGE, this.getCommand('/session/:sessionId/appium/device/current_package')], | ||
[this.QUERY_APP_STATE, this.postCommand('/session/:sessionId/appium/device/app_state')], | ||
[this.TOGGLE_AIRPLANE_MODE, this.postCommand('/session/:sessionId/appium/device/toggle_airplane_mode')], | ||
[this.TOGGLE_DATA, this.postCommand('/session/:sessionId/appium/device/toggle_data')], | ||
[this.TOGGLE_WIFI, this.postCommand('/session/:sessionId/appium/device/toggle_wifi')], | ||
[this.TOGGLE_LOCATION_SERVICES, this.postCommand('/session/:sessionId/appium/device/toggle_location_services')], | ||
[this.OPEN_NOTIFICATIONS, this.postCommand('/session/:sessionId/appium/device/open_notifications')], | ||
[this.START_ACTIVITY, this.postCommand('/session/:sessionId/appium/device/start_activity')], | ||
[this.GET_SYSTEM_BARS, this.getCommand('/session/:sessionId/appium/device/system_bars')], | ||
[this.GET_DISPLAY_DENSITY, this.getCommand('/session/:sessionId/appium/device/display_density')], | ||
[this.LAUNCH_APP, this.postCommand('/session/:sessionId/appium/app/launch')], | ||
[this.CLOSE_APP, this.postCommand('/session/:sessionId/appium/app/close')], | ||
[this.RESET_APP, this.postCommand('/session/:sessionId/appium/app/reset')], | ||
[this.BACKGROUND_APP, this.postCommand('/session/:sessionId/appium/app/background')], | ||
[this.GET_STRINGS, this.postCommand('/session/:sessionId/appium/app/strings')], | ||
[this.SET_VALUE_IMMEDIATE, this.postCommand('/session/:sessionId/appium/element/:elementId/value')], | ||
[this.REPLACE_VALUE, this.postCommand('/session/:sessionId/appium/element/:elementId/replace_value')], | ||
[this.SCREENSHOT, this.getCommand('/session/:sessionId/screenshot')], | ||
[this.ELEMENT_SCREENSHOT, this.getCommand('/session/:sessionId/element/:elementId/screenshot')], | ||
[this.GET_ACTIVE_ELEMENT, this.getCommand('/session/:sessionId/element/active')], | ||
]); | ||
|
||
private static postCommand(path: string): CommandInfo { | ||
return { method: 'POST', path }; | ||
} | ||
|
||
private static getCommand(path: string): CommandInfo { | ||
return { method: 'GET', path }; | ||
} | ||
|
||
private static deleteCommand(path: string): CommandInfo { | ||
return { method: 'DELETE', path }; | ||
} | ||
} |
Oops, something went wrong.