@samsara-dev/appwright 0.2.0
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/.eslintrc.js +4 -0
- package/CHANGELOG.md +538 -0
- package/LICENSE +202 -0
- package/README.md +183 -0
- package/dist/bin/index.d.ts +3 -0
- package/dist/bin/index.d.ts.map +1 -0
- package/dist/bin/index.js +53 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +65 -0
- package/dist/device/index.d.ts +171 -0
- package/dist/device/index.d.ts.map +1 -0
- package/dist/device/index.js +415 -0
- package/dist/fixture/index.d.ts +38 -0
- package/dist/fixture/index.d.ts.map +1 -0
- package/dist/fixture/index.js +78 -0
- package/dist/fixture/workerInfo.d.ts +27 -0
- package/dist/fixture/workerInfo.d.ts.map +1 -0
- package/dist/fixture/workerInfo.js +87 -0
- package/dist/global-setup.d.ts +5 -0
- package/dist/global-setup.d.ts.map +1 -0
- package/dist/global-setup.js +30 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/locator/index.d.ts +25 -0
- package/dist/locator/index.d.ts.map +1 -0
- package/dist/locator/index.js +296 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +19 -0
- package/dist/providers/appium.d.ts +15 -0
- package/dist/providers/appium.d.ts.map +1 -0
- package/dist/providers/appium.js +274 -0
- package/dist/providers/browserstack/index.d.ts +26 -0
- package/dist/providers/browserstack/index.d.ts.map +1 -0
- package/dist/providers/browserstack/index.js +272 -0
- package/dist/providers/browserstack/utils.d.ts +2 -0
- package/dist/providers/browserstack/utils.d.ts.map +1 -0
- package/dist/providers/browserstack/utils.js +34 -0
- package/dist/providers/emulator/index.d.ts +13 -0
- package/dist/providers/emulator/index.d.ts.map +1 -0
- package/dist/providers/emulator/index.js +86 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +31 -0
- package/dist/providers/lambdatest/index.d.ts +27 -0
- package/dist/providers/lambdatest/index.d.ts.map +1 -0
- package/dist/providers/lambdatest/index.js +280 -0
- package/dist/providers/lambdatest/utils.d.ts +3 -0
- package/dist/providers/lambdatest/utils.d.ts.map +1 -0
- package/dist/providers/lambdatest/utils.js +36 -0
- package/dist/providers/local/index.d.ts +13 -0
- package/dist/providers/local/index.d.ts.map +1 -0
- package/dist/providers/local/index.js +86 -0
- package/dist/reporter.d.ts +13 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +216 -0
- package/dist/tests/locator.spec.d.ts +2 -0
- package/dist/tests/locator.spec.d.ts.map +1 -0
- package/dist/tests/locator.spec.js +89 -0
- package/dist/tests/regex.spec.d.ts +2 -0
- package/dist/tests/regex.spec.d.ts.map +1 -0
- package/dist/tests/regex.spec.js +19 -0
- package/dist/tests/vitest.config.d.mts +3 -0
- package/dist/tests/vitest.config.d.mts.map +1 -0
- package/dist/tests/vitest.config.mjs +6 -0
- package/dist/types/errors.d.ts +7 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +15 -0
- package/dist/types/index.d.ts +234 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +66 -0
- package/dist/vision/index.d.ts +64 -0
- package/dist/vision/index.d.ts.map +1 -0
- package/dist/vision/index.js +106 -0
- package/package.json +63 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/device/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.Device = void 0;
|
|
38
|
+
const locator_1 = require("../locator");
|
|
39
|
+
const types_1 = require("../types");
|
|
40
|
+
const vision_1 = require("../vision");
|
|
41
|
+
const utils_1 = require("../utils");
|
|
42
|
+
const utils_2 = require("../providers/browserstack/utils");
|
|
43
|
+
const utils_3 = require("../providers/lambdatest/utils");
|
|
44
|
+
const logger_1 = require("../logger");
|
|
45
|
+
let Device = (() => {
|
|
46
|
+
let _instanceExtraInitializers = [];
|
|
47
|
+
let _tap_decorators;
|
|
48
|
+
let _terminateApp_decorators;
|
|
49
|
+
let _activateApp_decorators;
|
|
50
|
+
let _getClipboardText_decorators;
|
|
51
|
+
let _setMockCameraView_decorators;
|
|
52
|
+
let _pause_decorators;
|
|
53
|
+
let _waitForTimeout_decorators;
|
|
54
|
+
let _screenshot_decorators;
|
|
55
|
+
let _scroll_decorators;
|
|
56
|
+
let _sendKeyStrokes_decorators;
|
|
57
|
+
return class Device {
|
|
58
|
+
static {
|
|
59
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
60
|
+
_tap_decorators = [utils_1.boxedStep];
|
|
61
|
+
_terminateApp_decorators = [utils_1.boxedStep];
|
|
62
|
+
_activateApp_decorators = [utils_1.boxedStep];
|
|
63
|
+
_getClipboardText_decorators = [utils_1.boxedStep];
|
|
64
|
+
_setMockCameraView_decorators = [utils_1.boxedStep];
|
|
65
|
+
_pause_decorators = [utils_1.boxedStep];
|
|
66
|
+
_waitForTimeout_decorators = [utils_1.boxedStep];
|
|
67
|
+
_screenshot_decorators = [utils_1.boxedStep];
|
|
68
|
+
_scroll_decorators = [utils_1.boxedStep];
|
|
69
|
+
_sendKeyStrokes_decorators = [utils_1.boxedStep];
|
|
70
|
+
__esDecorate(this, null, _tap_decorators, { kind: "method", name: "tap", static: false, private: false, access: { has: obj => "tap" in obj, get: obj => obj.tap }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
71
|
+
__esDecorate(this, null, _terminateApp_decorators, { kind: "method", name: "terminateApp", static: false, private: false, access: { has: obj => "terminateApp" in obj, get: obj => obj.terminateApp }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
+
__esDecorate(this, null, _activateApp_decorators, { kind: "method", name: "activateApp", static: false, private: false, access: { has: obj => "activateApp" in obj, get: obj => obj.activateApp }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
|
+
__esDecorate(this, null, _getClipboardText_decorators, { kind: "method", name: "getClipboardText", static: false, private: false, access: { has: obj => "getClipboardText" in obj, get: obj => obj.getClipboardText }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
|
+
__esDecorate(this, null, _setMockCameraView_decorators, { kind: "method", name: "setMockCameraView", static: false, private: false, access: { has: obj => "setMockCameraView" in obj, get: obj => obj.setMockCameraView }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__esDecorate(this, null, _pause_decorators, { kind: "method", name: "pause", static: false, private: false, access: { has: obj => "pause" in obj, get: obj => obj.pause }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
76
|
+
__esDecorate(this, null, _waitForTimeout_decorators, { kind: "method", name: "waitForTimeout", static: false, private: false, access: { has: obj => "waitForTimeout" in obj, get: obj => obj.waitForTimeout }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
77
|
+
__esDecorate(this, null, _screenshot_decorators, { kind: "method", name: "screenshot", static: false, private: false, access: { has: obj => "screenshot" in obj, get: obj => obj.screenshot }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
78
|
+
__esDecorate(this, null, _scroll_decorators, { kind: "method", name: "scroll", static: false, private: false, access: { has: obj => "scroll" in obj, get: obj => obj.scroll }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
79
|
+
__esDecorate(this, null, _sendKeyStrokes_decorators, { kind: "method", name: "sendKeyStrokes", static: false, private: false, access: { has: obj => "sendKeyStrokes" in obj, get: obj => obj.sendKeyStrokes }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
80
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
81
|
+
}
|
|
82
|
+
webDriverClient = __runInitializers(this, _instanceExtraInitializers);
|
|
83
|
+
bundleId;
|
|
84
|
+
timeoutOpts;
|
|
85
|
+
provider;
|
|
86
|
+
constructor(webDriverClient, bundleId, timeoutOpts, provider) {
|
|
87
|
+
this.webDriverClient = webDriverClient;
|
|
88
|
+
this.bundleId = bundleId;
|
|
89
|
+
this.timeoutOpts = timeoutOpts;
|
|
90
|
+
this.provider = provider;
|
|
91
|
+
}
|
|
92
|
+
locator({ selector, findStrategy, textToMatch, }) {
|
|
93
|
+
return new locator_1.Locator(this.webDriverClient, this.timeoutOpts, selector, findStrategy, textToMatch);
|
|
94
|
+
}
|
|
95
|
+
vision() {
|
|
96
|
+
return new vision_1.VisionProvider(this, this.webDriverClient);
|
|
97
|
+
}
|
|
98
|
+
beta = {
|
|
99
|
+
tap: async (prompt, options) => {
|
|
100
|
+
return await this.vision().tap(prompt, options);
|
|
101
|
+
},
|
|
102
|
+
query: async (prompt, options) => {
|
|
103
|
+
return await this.vision().query(prompt, options);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Closes the automation session. This is called automatically after each test.
|
|
108
|
+
*
|
|
109
|
+
* **Usage:**
|
|
110
|
+
* ```js
|
|
111
|
+
* await device.close();
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async close() {
|
|
115
|
+
// TODO: Add @boxedStep decorator here
|
|
116
|
+
// Disabled because it breaks persistentDevice as test.step will throw as test is
|
|
117
|
+
// undefined when the function is called
|
|
118
|
+
try {
|
|
119
|
+
await this.webDriverClient.deleteSession();
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
logger_1.logger.error(`close:`, e);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Tap on the screen at the given coordinates, specified as x and y. The top left corner
|
|
127
|
+
* of the screen is { x: 0, y: 0 }.
|
|
128
|
+
*
|
|
129
|
+
* **Usage:**
|
|
130
|
+
* ```js
|
|
131
|
+
* await device.tap({ x: 100, y: 100 });
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @param coordinates to tap on
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
async tap({ x, y }) {
|
|
138
|
+
if (this.getPlatform() == types_1.Platform.ANDROID) {
|
|
139
|
+
await this.webDriverClient.executeScript("mobile: clickGesture", [
|
|
140
|
+
{
|
|
141
|
+
x: x,
|
|
142
|
+
y: y,
|
|
143
|
+
duration: 100,
|
|
144
|
+
tapCount: 1,
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
await this.webDriverClient.executeScript("mobile: tap", [
|
|
150
|
+
{
|
|
151
|
+
x: x,
|
|
152
|
+
y: y,
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Locate an element on the screen with text content. This method defaults to a
|
|
159
|
+
* substring match, and this be overridden by setting the `exact` option to `true`.
|
|
160
|
+
*
|
|
161
|
+
* **Usage:**
|
|
162
|
+
* ```js
|
|
163
|
+
* // with string
|
|
164
|
+
* const submitButton = device.getByText("Submit");
|
|
165
|
+
*
|
|
166
|
+
* // with RegExp
|
|
167
|
+
* const counter = device.getByText(/^Counter: \d+/);
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @param text string or regular expression to search for
|
|
171
|
+
* @param options
|
|
172
|
+
* @returns
|
|
173
|
+
*/
|
|
174
|
+
getByText(text, { exact = false } = {}) {
|
|
175
|
+
const isAndroid = this.getPlatform() == types_1.Platform.ANDROID;
|
|
176
|
+
if (text instanceof RegExp) {
|
|
177
|
+
const substringForContains = (0, utils_1.longestDeterministicGroup)(text);
|
|
178
|
+
if (!substringForContains) {
|
|
179
|
+
return this.locator({
|
|
180
|
+
selector: "//*",
|
|
181
|
+
findStrategy: "xpath",
|
|
182
|
+
textToMatch: text,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const selector = isAndroid
|
|
187
|
+
? `textContains("${substringForContains}")`
|
|
188
|
+
: `label CONTAINS "${substringForContains}"`;
|
|
189
|
+
return this.locator({
|
|
190
|
+
selector: selector,
|
|
191
|
+
findStrategy: isAndroid
|
|
192
|
+
? "-android uiautomator"
|
|
193
|
+
: "-ios predicate string",
|
|
194
|
+
textToMatch: text,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
let path;
|
|
199
|
+
if (isAndroid) {
|
|
200
|
+
path = exact ? `text("${text}")` : `textContains("${text}")`;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
path = exact ? `label == "${text}"` : `label CONTAINS "${text}"`;
|
|
204
|
+
}
|
|
205
|
+
return this.locator({
|
|
206
|
+
selector: path,
|
|
207
|
+
findStrategy: isAndroid
|
|
208
|
+
? "-android uiautomator"
|
|
209
|
+
: "-ios predicate string",
|
|
210
|
+
textToMatch: text,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Locate an element on the screen with accessibility identifier. This method defaults to
|
|
215
|
+
* a substring match, and this can be overridden by setting the `exact` option to `true`.
|
|
216
|
+
*
|
|
217
|
+
* **Usage:**
|
|
218
|
+
* ```js
|
|
219
|
+
* const element = await device.getById("signup_button");
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @param text string to search for
|
|
223
|
+
* @param options
|
|
224
|
+
* @returns
|
|
225
|
+
*/
|
|
226
|
+
getById(text, { exact = false } = {}) {
|
|
227
|
+
const isAndroid = this.getPlatform() == types_1.Platform.ANDROID;
|
|
228
|
+
let path;
|
|
229
|
+
if (isAndroid) {
|
|
230
|
+
path = exact ? `resourceId("${text}")` : `resourceIdMatches("${text}")`;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
path = exact ? `name == "${text}"` : `name CONTAINS "${text}"`;
|
|
234
|
+
}
|
|
235
|
+
return this.locator({
|
|
236
|
+
selector: path,
|
|
237
|
+
findStrategy: isAndroid
|
|
238
|
+
? "-android uiautomator"
|
|
239
|
+
: "-ios predicate string",
|
|
240
|
+
textToMatch: text,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Locate an element on the screen with xpath.
|
|
245
|
+
*
|
|
246
|
+
* **Usage:**
|
|
247
|
+
* ```js
|
|
248
|
+
* const element = await device.getByXpath(`//android.widget.Button[@text="Confirm"]`);
|
|
249
|
+
* ```
|
|
250
|
+
*
|
|
251
|
+
* @param xpath xpath to locate the element
|
|
252
|
+
* @returns
|
|
253
|
+
*/
|
|
254
|
+
getByXpath(xpath) {
|
|
255
|
+
return this.locator({ selector: xpath, findStrategy: "xpath" });
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Helper method to detect the mobile OS running on the device.
|
|
259
|
+
*
|
|
260
|
+
* **Usage:**
|
|
261
|
+
* ```js
|
|
262
|
+
* const platform = device.getPlatform();
|
|
263
|
+
* ```
|
|
264
|
+
*
|
|
265
|
+
* @returns "android" or "ios"
|
|
266
|
+
*/
|
|
267
|
+
getPlatform() {
|
|
268
|
+
const isAndroid = this.webDriverClient.isAndroid;
|
|
269
|
+
return isAndroid ? types_1.Platform.ANDROID : types_1.Platform.IOS;
|
|
270
|
+
}
|
|
271
|
+
async terminateApp(bundleId) {
|
|
272
|
+
if (!this.bundleId && !bundleId) {
|
|
273
|
+
throw new Error("bundleId is required to terminate the app.");
|
|
274
|
+
}
|
|
275
|
+
const keyName = this.getPlatform() == types_1.Platform.ANDROID ? "appId" : "bundleId";
|
|
276
|
+
await this.webDriverClient.executeScript("mobile: terminateApp", [
|
|
277
|
+
{
|
|
278
|
+
[keyName]: bundleId || this.bundleId,
|
|
279
|
+
},
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
async activateApp(bundleId) {
|
|
283
|
+
if (!this.bundleId && !bundleId) {
|
|
284
|
+
throw new Error("bundleId is required to activate the app.");
|
|
285
|
+
}
|
|
286
|
+
const keyName = this.getPlatform() == types_1.Platform.ANDROID ? "appId" : "bundleId";
|
|
287
|
+
await this.webDriverClient.executeScript("mobile: activateApp", [
|
|
288
|
+
{
|
|
289
|
+
[keyName]: bundleId || this.bundleId,
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Retrieves text content from the clipboard of the mobile device. This is useful
|
|
295
|
+
* after a "copy to clipboard" action has been performed. This returns base64 encoded string.
|
|
296
|
+
*
|
|
297
|
+
* **Usage:**
|
|
298
|
+
* ```js
|
|
299
|
+
* const clipboardText = await device.getClipboardText();
|
|
300
|
+
* ```
|
|
301
|
+
*
|
|
302
|
+
* @returns Returns the text content of the clipboard in base64 encoded string.
|
|
303
|
+
*/
|
|
304
|
+
async getClipboardText() {
|
|
305
|
+
if (this.getPlatform() == types_1.Platform.ANDROID) {
|
|
306
|
+
return await this.webDriverClient.getClipboard();
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
if (this.provider == "emulator") {
|
|
310
|
+
// iOS simulator supports clipboard sharing
|
|
311
|
+
return await this.webDriverClient.getClipboard();
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
if (!this.bundleId) {
|
|
315
|
+
throw new Error("bundleId is required to retrieve clipboard data on a real device.");
|
|
316
|
+
}
|
|
317
|
+
await this.activateApp("com.facebook.WebDriverAgentRunner.xctrunner");
|
|
318
|
+
const clipboardDataBase64 = await this.webDriverClient.getClipboard();
|
|
319
|
+
await this.activateApp(this.bundleId);
|
|
320
|
+
return clipboardDataBase64;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Sets a mock camera view using the specified image. This injects a mock image into the camera view.
|
|
326
|
+
* Currently, this functionality is supported only for BrowserStack.
|
|
327
|
+
*
|
|
328
|
+
* **Usage:**
|
|
329
|
+
* ```js
|
|
330
|
+
* await device.setMockCameraView(`screenshot.png`);
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* @param imagePath path to the image file that will be used as the mock camera view.
|
|
334
|
+
* @returns
|
|
335
|
+
*/
|
|
336
|
+
async setMockCameraView(imagePath) {
|
|
337
|
+
if (this.provider == "browserstack") {
|
|
338
|
+
const imageURL = await (0, utils_2.uploadImageToBS)(imagePath);
|
|
339
|
+
await this.webDriverClient.executeScript(`browserstack_executor: {"action":"cameraImageInjection", "arguments": {"imageUrl" : "${imageURL}"}}`, []);
|
|
340
|
+
}
|
|
341
|
+
else if (this.provider == "lambdatest") {
|
|
342
|
+
const imageURL = await (0, utils_3.uploadImageToLambdaTest)(imagePath);
|
|
343
|
+
await this.webDriverClient.executeScript(`lambda-image-injection=${imageURL}`, []);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async pause() {
|
|
347
|
+
const skipPause = process.env.CI === "true";
|
|
348
|
+
if (skipPause) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
logger_1.logger.log(`device.pause: Use Appium Inspector to attach to the session.`);
|
|
352
|
+
let iterations = 0;
|
|
353
|
+
// eslint-disable-next-line no-constant-condition
|
|
354
|
+
while (true) {
|
|
355
|
+
await new Promise((resolve) => setTimeout(resolve, 20_000));
|
|
356
|
+
await this.webDriverClient.takeScreenshot();
|
|
357
|
+
iterations += 1;
|
|
358
|
+
if (iterations % 3 === 0) {
|
|
359
|
+
logger_1.logger.log(`device.pause: ${iterations * 20} secs elapsed.`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async waitForTimeout(timeout) {
|
|
364
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get a screenshot of the current screen as a base64 encoded string.
|
|
368
|
+
*/
|
|
369
|
+
async screenshot() {
|
|
370
|
+
return await this.webDriverClient.takeScreenshot();
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* [iOS Only]
|
|
374
|
+
* Scroll the screen from 0.2 to 0.8 of the screen height.
|
|
375
|
+
* This can be used for controlled scroll, for auto scroll checkout `scroll` method from locator.
|
|
376
|
+
*
|
|
377
|
+
* **Usage:**
|
|
378
|
+
* ```js
|
|
379
|
+
* await device.scroll();
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
*/
|
|
383
|
+
async scroll() {
|
|
384
|
+
const driverSize = await this.webDriverClient.getWindowRect();
|
|
385
|
+
// Scrolls from 0.8 to 0.2 of the screen height
|
|
386
|
+
const from = { x: driverSize.width / 2, y: driverSize.height * 0.8 };
|
|
387
|
+
const to = { x: driverSize.width / 2, y: driverSize.height * 0.2 };
|
|
388
|
+
await this.webDriverClient.executeScript("mobile: dragFromToForDuration", [
|
|
389
|
+
{ duration: 2, fromX: from.x, fromY: from.y, toX: to.x, toY: to.y },
|
|
390
|
+
]);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Send keys to already focused input field.
|
|
394
|
+
* To fill input fields using the selectors use `sendKeyStrokes` method from locator
|
|
395
|
+
*/
|
|
396
|
+
async sendKeyStrokes(value) {
|
|
397
|
+
const actions = value
|
|
398
|
+
.split("")
|
|
399
|
+
.map((char) => [
|
|
400
|
+
{ type: "keyDown", value: char },
|
|
401
|
+
{ type: "keyUp", value: char },
|
|
402
|
+
])
|
|
403
|
+
.flat();
|
|
404
|
+
await this.webDriverClient.performActions([
|
|
405
|
+
{
|
|
406
|
+
type: "key",
|
|
407
|
+
id: "keyboard",
|
|
408
|
+
actions: actions,
|
|
409
|
+
},
|
|
410
|
+
]);
|
|
411
|
+
await this.webDriverClient.releaseActions();
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
})();
|
|
415
|
+
exports.Device = Device;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AppwrightLocator, DeviceProvider, ActionOptions } from "../types";
|
|
2
|
+
import { Device } from "../device";
|
|
3
|
+
type TestLevelFixtures = {
|
|
4
|
+
/**
|
|
5
|
+
* Device provider to be used for the test.
|
|
6
|
+
* This creates and manages the device lifecycle for the test
|
|
7
|
+
*/
|
|
8
|
+
deviceProvider: DeviceProvider;
|
|
9
|
+
/**
|
|
10
|
+
* The device instance that will be used for running the test.
|
|
11
|
+
* This provides the functionality to interact with the device
|
|
12
|
+
* during the test.
|
|
13
|
+
*/
|
|
14
|
+
device: Device;
|
|
15
|
+
};
|
|
16
|
+
type WorkerLevelFixtures = {
|
|
17
|
+
persistentDevice: Device;
|
|
18
|
+
};
|
|
19
|
+
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & TestLevelFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & WorkerLevelFixtures>;
|
|
20
|
+
/**
|
|
21
|
+
* Function to extend Playwright’s expect assertion capabilities.
|
|
22
|
+
* This adds a new method `toBeVisible` which checks if an element is visible on the screen.
|
|
23
|
+
*
|
|
24
|
+
* @param locator The AppwrightLocator that locates the element on the device screen.
|
|
25
|
+
* @param options
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
export declare const expect: import("@playwright/test").Expect<{
|
|
29
|
+
toBeVisible: (locator: AppwrightLocator, options?: ActionOptions) => Promise<{
|
|
30
|
+
message: () => "" | "Element was not found on the screen";
|
|
31
|
+
pass: boolean;
|
|
32
|
+
name: string;
|
|
33
|
+
expected: boolean;
|
|
34
|
+
actual: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
}>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixture/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEd,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAKnC,KAAK,iBAAiB,GAAG;IACvB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAE/B;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,IAAI,uRA4Df,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;2BACY,gBAAgB,YAAY,aAAa;;;;;;;EAUtE,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.expect = exports.test = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const providers_1 = require("../providers");
|
|
6
|
+
const workerInfo_1 = require("./workerInfo");
|
|
7
|
+
const appium_1 = require("../providers/appium");
|
|
8
|
+
exports.test = test_1.test.extend({
|
|
9
|
+
deviceProvider: async ({}, use, testInfo) => {
|
|
10
|
+
const deviceProvider = (0, providers_1.createDeviceProvider)(testInfo.project);
|
|
11
|
+
await use(deviceProvider);
|
|
12
|
+
},
|
|
13
|
+
device: async ({ deviceProvider }, use, testInfo) => {
|
|
14
|
+
const device = await deviceProvider.getDevice();
|
|
15
|
+
const deviceProviderName = testInfo.project.use.device?.provider;
|
|
16
|
+
testInfo.annotations.push({
|
|
17
|
+
type: "providerName",
|
|
18
|
+
description: deviceProviderName,
|
|
19
|
+
});
|
|
20
|
+
testInfo.annotations.push({
|
|
21
|
+
type: "sessionId",
|
|
22
|
+
description: deviceProvider.sessionId,
|
|
23
|
+
});
|
|
24
|
+
await deviceProvider.syncTestDetails?.({ name: testInfo.title });
|
|
25
|
+
await use(device);
|
|
26
|
+
await device.close();
|
|
27
|
+
if (deviceProviderName === "emulator" ||
|
|
28
|
+
deviceProviderName === "local-device") {
|
|
29
|
+
await (0, appium_1.stopAppiumServer)();
|
|
30
|
+
}
|
|
31
|
+
await deviceProvider.syncTestDetails?.({
|
|
32
|
+
name: testInfo.title,
|
|
33
|
+
status: testInfo.status,
|
|
34
|
+
reason: testInfo.error?.message,
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
persistentDevice: [
|
|
38
|
+
async ({}, use, workerInfo) => {
|
|
39
|
+
const { project, workerIndex } = workerInfo;
|
|
40
|
+
const beforeSession = new Date();
|
|
41
|
+
const deviceProvider = (0, providers_1.createDeviceProvider)(project);
|
|
42
|
+
const device = await deviceProvider.getDevice();
|
|
43
|
+
const sessionId = deviceProvider.sessionId;
|
|
44
|
+
if (!sessionId) {
|
|
45
|
+
throw new Error("Worker must have a sessionId.");
|
|
46
|
+
}
|
|
47
|
+
const providerName = project.use.device
|
|
48
|
+
?.provider;
|
|
49
|
+
const afterSession = new Date();
|
|
50
|
+
const workerInfoStore = new workerInfo_1.WorkerInfoStore();
|
|
51
|
+
await workerInfoStore.saveWorkerStartTime(workerIndex, sessionId, providerName, beforeSession, afterSession);
|
|
52
|
+
await use(device);
|
|
53
|
+
await workerInfoStore.saveWorkerEndTime(workerIndex, new Date());
|
|
54
|
+
await device.close();
|
|
55
|
+
},
|
|
56
|
+
{ scope: "worker" },
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Function to extend Playwright’s expect assertion capabilities.
|
|
61
|
+
* This adds a new method `toBeVisible` which checks if an element is visible on the screen.
|
|
62
|
+
*
|
|
63
|
+
* @param locator The AppwrightLocator that locates the element on the device screen.
|
|
64
|
+
* @param options
|
|
65
|
+
* @returns
|
|
66
|
+
*/
|
|
67
|
+
exports.expect = exports.test.expect.extend({
|
|
68
|
+
toBeVisible: async (locator, options) => {
|
|
69
|
+
const isVisible = await locator.isVisible(options);
|
|
70
|
+
return {
|
|
71
|
+
message: () => (isVisible ? "" : `Element was not found on the screen`),
|
|
72
|
+
pass: isVisible,
|
|
73
|
+
name: "toBeVisible",
|
|
74
|
+
expected: true,
|
|
75
|
+
actual: isVisible,
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type TestInWorkerInfo = {
|
|
2
|
+
title: string;
|
|
3
|
+
startTime: string;
|
|
4
|
+
};
|
|
5
|
+
export type WorkerInfo = {
|
|
6
|
+
idx: number;
|
|
7
|
+
sessionId?: string | undefined;
|
|
8
|
+
providerName?: string | undefined;
|
|
9
|
+
startTime?: {
|
|
10
|
+
beforeAppiumSession: string;
|
|
11
|
+
afterAppiumSession: string;
|
|
12
|
+
} | undefined;
|
|
13
|
+
endTime?: string | undefined;
|
|
14
|
+
tests: TestInWorkerInfo[];
|
|
15
|
+
};
|
|
16
|
+
export declare class WorkerInfoStore {
|
|
17
|
+
private basePath;
|
|
18
|
+
constructor();
|
|
19
|
+
saveWorkerToDisk(idx: number, contents: WorkerInfo): Promise<void>;
|
|
20
|
+
getWorkerFromDisk(idx: number): Promise<WorkerInfo | undefined>;
|
|
21
|
+
getWorkerStartTime(idx: number): Promise<Date>;
|
|
22
|
+
saveWorkerStartTime(idx: number, sessionId: string, providerName: string, beforeAppiumSession: Date, afterAppiumSession: Date): Promise<void>;
|
|
23
|
+
saveWorkerEndTime(idx: number, endTime: Date): Promise<void>;
|
|
24
|
+
saveTestStartTime(idx: number, testTitle: string, startTime: Date): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=workerInfo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerInfo.d.ts","sourceRoot":"","sources":["../../src/fixture/workerInfo.ts"],"names":[],"mappings":"AAIA,KAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EACN;QAEE,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;KAC5B,GACD,SAAS,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAS;;IAMnB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU;IAYlD,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAQ/D,kBAAkB,CAAC,GAAG,EAAE,MAAM;IAQ9B,mBAAmB,CACvB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,IAAI,EACzB,kBAAkB,EAAE,IAAI;IA0BpB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;IAS5C,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI;CAexE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WorkerInfoStore = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
class WorkerInfoStore {
|
|
11
|
+
basePath;
|
|
12
|
+
constructor() {
|
|
13
|
+
this.basePath = (0, utils_1.basePath)();
|
|
14
|
+
}
|
|
15
|
+
async saveWorkerToDisk(idx, contents) {
|
|
16
|
+
if (!fs_1.default.existsSync(this.basePath)) {
|
|
17
|
+
fs_1.default.mkdirSync(this.basePath, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
// TODO: can we make this file path unique for a session?
|
|
20
|
+
// will avoidd ios/android running into issues when running concurrently on local
|
|
21
|
+
fs_1.default.writeFileSync(path_1.default.join(this.basePath, `worker-info-${idx}.json`), JSON.stringify(contents, null, 2));
|
|
22
|
+
}
|
|
23
|
+
async getWorkerFromDisk(idx) {
|
|
24
|
+
const filePath = path_1.default.join(this.basePath, `worker-info-${idx}.json`);
|
|
25
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, "utf-8"));
|
|
29
|
+
}
|
|
30
|
+
async getWorkerStartTime(idx) {
|
|
31
|
+
const info = await this.getWorkerFromDisk(idx);
|
|
32
|
+
if (!info || !info.startTime) {
|
|
33
|
+
throw new Error(`Worker start time info is not available.`);
|
|
34
|
+
}
|
|
35
|
+
return new Date(info.startTime.afterAppiumSession);
|
|
36
|
+
}
|
|
37
|
+
async saveWorkerStartTime(idx, sessionId, providerName, beforeAppiumSession, afterAppiumSession) {
|
|
38
|
+
let info = await this.getWorkerFromDisk(idx);
|
|
39
|
+
const delta = {
|
|
40
|
+
providerName,
|
|
41
|
+
sessionId,
|
|
42
|
+
startTime: {
|
|
43
|
+
beforeAppiumSession: beforeAppiumSession.toISOString(),
|
|
44
|
+
afterAppiumSession: afterAppiumSession.toISOString(),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
if (!info) {
|
|
48
|
+
info = {
|
|
49
|
+
...delta,
|
|
50
|
+
idx,
|
|
51
|
+
tests: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
info = {
|
|
56
|
+
...info,
|
|
57
|
+
...delta,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return this.saveWorkerToDisk(idx, info);
|
|
61
|
+
}
|
|
62
|
+
async saveWorkerEndTime(idx, endTime) {
|
|
63
|
+
let info = await this.getWorkerFromDisk(idx);
|
|
64
|
+
if (!info) {
|
|
65
|
+
throw new Error(`Worker info not found for idx: ${idx}`);
|
|
66
|
+
}
|
|
67
|
+
info.endTime = endTime.toISOString();
|
|
68
|
+
return this.saveWorkerToDisk(idx, info);
|
|
69
|
+
}
|
|
70
|
+
async saveTestStartTime(idx, testTitle, startTime) {
|
|
71
|
+
let info = await this.getWorkerFromDisk(idx);
|
|
72
|
+
if (!info) {
|
|
73
|
+
info = {
|
|
74
|
+
idx,
|
|
75
|
+
tests: [{ title: testTitle, startTime: startTime.toISOString() }],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
info.tests.push({
|
|
80
|
+
title: testTitle,
|
|
81
|
+
startTime: startTime.toISOString(),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return this.saveWorkerToDisk(idx, info);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.WorkerInfoStore = WorkerInfoStore;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../src/global-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C,iBAAe,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,iBA4B7D;AAED,eAAe,WAAW,CAAC"}
|