@qapilot/appium-module 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -148
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @qapilot/appium-module
|
|
2
2
|
|
|
3
|
-
Appium W3C WebDriver
|
|
3
|
+
A unified Appium W3C WebDriver client for **BrowserStack**, **LambdaTest**, and **local Appium** servers. One `invokeCommand` API, any provider, full TypeScript support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,22 +8,21 @@ Appium W3C WebDriver module for interacting with devices across **BrowserStack**
|
|
|
8
8
|
npm install @qapilot/appium-module
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
No additional `@types` package is needed — types are bundled with the package.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
npm install express
|
|
15
|
-
```
|
|
13
|
+
---
|
|
16
14
|
|
|
17
15
|
## Usage
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Import `invokeCommand` and call any W3C WebDriver command against any supported provider.
|
|
17
|
+
Import `invokeCommand` and call any supported command against any provider.
|
|
22
18
|
|
|
23
19
|
```typescript
|
|
24
20
|
import { invokeCommand } from '@qapilot/appium-module';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### BrowserStack
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
```typescript
|
|
27
26
|
const session = await invokeCommand({
|
|
28
27
|
commandName: 'createSession',
|
|
29
28
|
provider: 'browserstack',
|
|
@@ -40,101 +39,88 @@ const session = await invokeCommand({
|
|
|
40
39
|
},
|
|
41
40
|
},
|
|
42
41
|
});
|
|
43
|
-
|
|
44
|
-
if (!session.success) {
|
|
45
|
-
throw new Error(session.error?.message);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { sessionId } = session.data;
|
|
49
|
-
|
|
50
|
-
// Find an element
|
|
51
|
-
const element = await invokeCommand({
|
|
52
|
-
commandName: 'findElement',
|
|
53
|
-
provider: 'browserstack',
|
|
54
|
-
credentials: { username: process.env.BS_USER, accessKey: process.env.BS_KEY },
|
|
55
|
-
params: { sessionId, using: 'accessibility id', value: 'loginButton' },
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Click it
|
|
59
|
-
await invokeCommand({
|
|
60
|
-
commandName: 'clickElement',
|
|
61
|
-
provider: 'browserstack',
|
|
62
|
-
credentials: { username: process.env.BS_USER, accessKey: process.env.BS_KEY },
|
|
63
|
-
params: { sessionId, elementId: element.data.elementId },
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Take a screenshot
|
|
67
|
-
const screenshot = await invokeCommand({
|
|
68
|
-
commandName: 'getScreenshot',
|
|
69
|
-
provider: 'browserstack',
|
|
70
|
-
credentials: { username: process.env.BS_USER, accessKey: process.env.BS_KEY },
|
|
71
|
-
params: { sessionId },
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Delete the session
|
|
75
|
-
await invokeCommand({
|
|
76
|
-
commandName: 'deleteSession',
|
|
77
|
-
provider: 'browserstack',
|
|
78
|
-
credentials: { username: process.env.BS_USER, accessKey: process.env.BS_KEY },
|
|
79
|
-
params: { sessionId },
|
|
80
|
-
});
|
|
81
42
|
```
|
|
82
43
|
|
|
83
|
-
###
|
|
44
|
+
### LambdaTest
|
|
84
45
|
|
|
85
46
|
```typescript
|
|
86
|
-
import { invokeCommand } from '@qapilot/appium-module';
|
|
87
|
-
|
|
88
47
|
const session = await invokeCommand({
|
|
89
48
|
commandName: 'createSession',
|
|
90
|
-
provider: '
|
|
49
|
+
provider: 'lambdatest',
|
|
91
50
|
credentials: {
|
|
92
|
-
|
|
51
|
+
username: process.env.LT_USER,
|
|
52
|
+
accessKey: process.env.LT_KEY,
|
|
93
53
|
},
|
|
94
54
|
params: {
|
|
95
55
|
capabilities: {
|
|
96
56
|
platformName: 'Android',
|
|
97
|
-
'appium:
|
|
98
|
-
'appium:
|
|
57
|
+
'appium:deviceName': 'Galaxy S21',
|
|
58
|
+
'appium:app': 'lt://<app-id>',
|
|
99
59
|
},
|
|
100
60
|
},
|
|
101
61
|
});
|
|
102
62
|
```
|
|
103
63
|
|
|
104
|
-
###
|
|
64
|
+
### Local Appium
|
|
105
65
|
|
|
106
66
|
```typescript
|
|
107
|
-
import { invokeCommand } from '@qapilot/appium-module';
|
|
108
|
-
|
|
109
67
|
const session = await invokeCommand({
|
|
110
68
|
commandName: 'createSession',
|
|
111
|
-
provider: '
|
|
69
|
+
provider: 'local',
|
|
112
70
|
credentials: {
|
|
113
|
-
|
|
114
|
-
accessKey: process.env.LT_KEY,
|
|
71
|
+
hubUrl: 'http://localhost:4723',
|
|
115
72
|
},
|
|
116
73
|
params: {
|
|
117
74
|
capabilities: {
|
|
118
75
|
platformName: 'Android',
|
|
119
|
-
'appium:
|
|
120
|
-
'appium:
|
|
76
|
+
'appium:automationName': 'UiAutomator2',
|
|
77
|
+
'appium:deviceName': 'emulator-5554',
|
|
121
78
|
},
|
|
122
79
|
},
|
|
123
80
|
});
|
|
124
81
|
```
|
|
125
82
|
|
|
83
|
+
---
|
|
84
|
+
|
|
126
85
|
## Supported Commands
|
|
127
86
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
|
|
132
|
-
|
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
|
87
|
+
All commands are invoked via `invokeCommand({ commandName, provider, credentials, params })`.
|
|
88
|
+
|
|
89
|
+
| Category | Command | Key `params` |
|
|
90
|
+
|----------|---------|--------------|
|
|
91
|
+
| **Session** | `createSession` | `capabilities` |
|
|
92
|
+
| | `getSession` | `sessionId` |
|
|
93
|
+
| | `deleteSession` | `sessionId` |
|
|
94
|
+
| **Element** | `findElement` | `sessionId`, `using`, `value` |
|
|
95
|
+
| | `findElements` | `sessionId`, `using`, `value` |
|
|
96
|
+
| | `clickElement` | `sessionId`, `elementId` |
|
|
97
|
+
| | `setValue` | `sessionId`, `elementId`, `text` |
|
|
98
|
+
| | `clearElement` | `sessionId`, `elementId` |
|
|
99
|
+
| | `getElementText` | `sessionId`, `elementId` |
|
|
100
|
+
| | `getElementAttribute` | `sessionId`, `elementId`, `attribute` |
|
|
101
|
+
| | `getElementDisplayed` | `sessionId`, `elementId` |
|
|
102
|
+
| | `getElementRect` | `sessionId`, `elementId` |
|
|
103
|
+
| | `getElementLocation` | `sessionId`, `elementId` |
|
|
104
|
+
| | `getElementSize` | `sessionId`, `elementId` |
|
|
105
|
+
| **Page** | `getScreenshot` | `sessionId` |
|
|
106
|
+
| | `getSource` | `sessionId` |
|
|
107
|
+
| **Actions** | `performActions` | `sessionId`, `actions` |
|
|
108
|
+
| | `touchPerform` | `sessionId`, `actions` |
|
|
109
|
+
| **Context** | `getContext` | `sessionId` |
|
|
110
|
+
| | `setContext` | `sessionId`, `name` |
|
|
111
|
+
| | `getContexts` | `sessionId` |
|
|
112
|
+
| **Window** | `getWindow` | `sessionId` |
|
|
113
|
+
| | `getWindowHandles` | `sessionId` |
|
|
114
|
+
| | `switchWindow` | `sessionId`, `handle` |
|
|
115
|
+
| **Device** | `hideKeyboard` | `sessionId` |
|
|
116
|
+
| | `isKeyboardShown` | `sessionId` |
|
|
117
|
+
| | `pressKeycode` | `sessionId`, `keycode` |
|
|
118
|
+
| | `activateApp` | `sessionId`, `appId` |
|
|
119
|
+
| | `terminateApp` | `sessionId`, `appId` |
|
|
120
|
+
| | `setOrientation` | `sessionId`, `orientation` |
|
|
121
|
+
| | `setLocation` | `sessionId`, `latitude`, `longitude` |
|
|
122
|
+
| **Script** | `executeScript` | `sessionId`, `script`, `args` |
|
|
123
|
+
| | `executeScriptSync` | `sessionId`, `script`, `args` |
|
|
138
124
|
|
|
139
125
|
Get the full list at runtime:
|
|
140
126
|
|
|
@@ -143,46 +129,56 @@ import { getSupportedCommands } from '@qapilot/appium-module';
|
|
|
143
129
|
console.log(getSupportedCommands());
|
|
144
130
|
```
|
|
145
131
|
|
|
146
|
-
|
|
132
|
+
---
|
|
147
133
|
|
|
148
|
-
|
|
134
|
+
## Response Shape
|
|
135
|
+
|
|
136
|
+
Every `invokeCommand` call returns an `ExecuteResponse<T>`:
|
|
149
137
|
|
|
150
138
|
```typescript
|
|
151
|
-
|
|
152
|
-
|
|
139
|
+
{
|
|
140
|
+
success: boolean;
|
|
141
|
+
data?: T; // present when success is true
|
|
142
|
+
error?: { // present when success is false
|
|
143
|
+
message: string;
|
|
144
|
+
statusCode?: number;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
```
|
|
153
148
|
|
|
154
|
-
|
|
155
|
-
readonly name = 'saucelabs' as any;
|
|
156
|
-
protected readonly hubBaseUrl = 'https://ondemand.us-west-1.saucelabs.com';
|
|
157
|
-
protected readonly wdHubPath = '/wd/hub';
|
|
149
|
+
Always check `success` before accessing `data`:
|
|
158
150
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
151
|
+
```typescript
|
|
152
|
+
const result = await invokeCommand({ commandName: 'findElement', ... });
|
|
162
153
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Authorization: this.buildBasicAuthHeader(credentials),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
154
|
+
if (!result.success) {
|
|
155
|
+
throw new Error(result.error?.message);
|
|
156
|
+
}
|
|
169
157
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
158
|
+
const { elementId } = result.data;
|
|
159
|
+
```
|
|
173
160
|
|
|
174
|
-
|
|
175
|
-
return this.mapW3CError(statusCode, body);
|
|
176
|
-
}
|
|
161
|
+
---
|
|
177
162
|
|
|
178
|
-
|
|
179
|
-
return {};
|
|
180
|
-
}
|
|
181
|
-
}
|
|
163
|
+
## TypeScript Types
|
|
182
164
|
|
|
183
|
-
|
|
165
|
+
All types are exported from the main entry point:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import type {
|
|
169
|
+
ExecuteRequest,
|
|
170
|
+
ExecuteResponse,
|
|
171
|
+
ProviderName,
|
|
172
|
+
ProviderCredentials,
|
|
173
|
+
AppiumServiceError,
|
|
174
|
+
IAppiumProvider,
|
|
175
|
+
} from '@qapilot/appium-module';
|
|
184
176
|
```
|
|
185
177
|
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Extending
|
|
181
|
+
|
|
186
182
|
### Custom Commands
|
|
187
183
|
|
|
188
184
|
```typescript
|
|
@@ -205,64 +201,45 @@ class ShakeDeviceCommand extends BaseCommand<{ sessionId: string }, void> {
|
|
|
205
201
|
registerCommand(new ShakeDeviceCommand());
|
|
206
202
|
```
|
|
207
203
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
The package also ships a standalone Express HTTP server that exposes the command invoker over REST. This is useful when you want to call Appium from a language other than Node.js, or from a microservice over HTTP.
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
# Run the server directly
|
|
214
|
-
npx @qapilot/appium-module/server
|
|
215
|
-
|
|
216
|
-
# Or in your own project (after installing express):
|
|
217
|
-
node node_modules/@qapilot/appium-module/dist/server.js
|
|
218
|
-
```
|
|
204
|
+
### Custom Providers
|
|
219
205
|
|
|
220
|
-
|
|
206
|
+
```typescript
|
|
207
|
+
import { registerProvider, BaseProvider } from '@qapilot/appium-module';
|
|
208
|
+
import type { IAppiumProvider, ProviderCredentials, AppiumServiceError } from '@qapilot/appium-module';
|
|
221
209
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
"credentials": { "username": "...", "accessKey": "..." },
|
|
227
|
-
"params": { "sessionId": "abc123", "using": "xpath", "value": "//button" }
|
|
228
|
-
}
|
|
229
|
-
```
|
|
210
|
+
class SauceLabsProvider extends BaseProvider implements IAppiumProvider {
|
|
211
|
+
readonly name = 'saucelabs' as any;
|
|
212
|
+
protected readonly hubBaseUrl = 'https://ondemand.us-west-1.saucelabs.com';
|
|
213
|
+
protected readonly wdHubPath = '/wd/hub';
|
|
230
214
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
215
|
+
buildUrl(_sessionId: string | null, path: string, credentials: ProviderCredentials): string {
|
|
216
|
+
return `${this.buildBaseUrl(credentials)}${path}`;
|
|
217
|
+
}
|
|
234
218
|
|
|
235
|
-
|
|
219
|
+
buildHeaders(credentials: ProviderCredentials): Record<string, string> {
|
|
220
|
+
return {
|
|
221
|
+
'Content-Type': 'application/json',
|
|
222
|
+
Authorization: this.buildBasicAuthHeader(credentials),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
236
225
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
| `NODE_ENV` | `development` | Set to `production` for JSON logging |
|
|
241
|
-
| `LOG_LEVEL` | `info` | Pino log level |
|
|
242
|
-
| `LOCAL_APPIUM_URL` | `http://localhost:4723` | Default URL for the `local` provider |
|
|
226
|
+
normalizeCapabilities(caps: Record<string, unknown>): Record<string, unknown> {
|
|
227
|
+
return caps;
|
|
228
|
+
}
|
|
243
229
|
|
|
244
|
-
|
|
230
|
+
normalizeError(statusCode: number, body: unknown): AppiumServiceError {
|
|
231
|
+
return this.mapW3CError(statusCode, body);
|
|
232
|
+
}
|
|
245
233
|
|
|
246
|
-
|
|
234
|
+
getDefaultCapabilities(): Record<string, unknown> {
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
247
238
|
|
|
248
|
-
|
|
249
|
-
import type {
|
|
250
|
-
ExecuteRequest,
|
|
251
|
-
ExecuteResponse,
|
|
252
|
-
ProviderName,
|
|
253
|
-
ProviderCredentials,
|
|
254
|
-
AppiumServiceError,
|
|
255
|
-
IAppiumProvider,
|
|
256
|
-
} from '@qapilot/appium-module';
|
|
239
|
+
registerProvider(new SauceLabsProvider());
|
|
257
240
|
```
|
|
258
241
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
npm run build # compile TypeScript to dist/
|
|
263
|
-
npm login # authenticate with npm
|
|
264
|
-
npm publish --access public
|
|
265
|
-
```
|
|
242
|
+
---
|
|
266
243
|
|
|
267
244
|
## License
|
|
268
245
|
|
package/package.json
CHANGED