@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.
Files changed (81) hide show
  1. package/.eslintrc.js +4 -0
  2. package/CHANGELOG.md +538 -0
  3. package/LICENSE +202 -0
  4. package/README.md +183 -0
  5. package/dist/bin/index.d.ts +3 -0
  6. package/dist/bin/index.d.ts.map +1 -0
  7. package/dist/bin/index.js +53 -0
  8. package/dist/config.d.ts +4 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +65 -0
  11. package/dist/device/index.d.ts +171 -0
  12. package/dist/device/index.d.ts.map +1 -0
  13. package/dist/device/index.js +415 -0
  14. package/dist/fixture/index.d.ts +38 -0
  15. package/dist/fixture/index.d.ts.map +1 -0
  16. package/dist/fixture/index.js +78 -0
  17. package/dist/fixture/workerInfo.d.ts +27 -0
  18. package/dist/fixture/workerInfo.d.ts.map +1 -0
  19. package/dist/fixture/workerInfo.js +87 -0
  20. package/dist/global-setup.d.ts +5 -0
  21. package/dist/global-setup.d.ts.map +1 -0
  22. package/dist/global-setup.js +30 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +25 -0
  26. package/dist/locator/index.d.ts +25 -0
  27. package/dist/locator/index.d.ts.map +1 -0
  28. package/dist/locator/index.js +296 -0
  29. package/dist/logger.d.ts +9 -0
  30. package/dist/logger.d.ts.map +1 -0
  31. package/dist/logger.js +19 -0
  32. package/dist/providers/appium.d.ts +15 -0
  33. package/dist/providers/appium.d.ts.map +1 -0
  34. package/dist/providers/appium.js +274 -0
  35. package/dist/providers/browserstack/index.d.ts +26 -0
  36. package/dist/providers/browserstack/index.d.ts.map +1 -0
  37. package/dist/providers/browserstack/index.js +272 -0
  38. package/dist/providers/browserstack/utils.d.ts +2 -0
  39. package/dist/providers/browserstack/utils.d.ts.map +1 -0
  40. package/dist/providers/browserstack/utils.js +34 -0
  41. package/dist/providers/emulator/index.d.ts +13 -0
  42. package/dist/providers/emulator/index.d.ts.map +1 -0
  43. package/dist/providers/emulator/index.js +86 -0
  44. package/dist/providers/index.d.ts +5 -0
  45. package/dist/providers/index.d.ts.map +1 -0
  46. package/dist/providers/index.js +31 -0
  47. package/dist/providers/lambdatest/index.d.ts +27 -0
  48. package/dist/providers/lambdatest/index.d.ts.map +1 -0
  49. package/dist/providers/lambdatest/index.js +280 -0
  50. package/dist/providers/lambdatest/utils.d.ts +3 -0
  51. package/dist/providers/lambdatest/utils.d.ts.map +1 -0
  52. package/dist/providers/lambdatest/utils.js +36 -0
  53. package/dist/providers/local/index.d.ts +13 -0
  54. package/dist/providers/local/index.d.ts.map +1 -0
  55. package/dist/providers/local/index.js +86 -0
  56. package/dist/reporter.d.ts +13 -0
  57. package/dist/reporter.d.ts.map +1 -0
  58. package/dist/reporter.js +216 -0
  59. package/dist/tests/locator.spec.d.ts +2 -0
  60. package/dist/tests/locator.spec.d.ts.map +1 -0
  61. package/dist/tests/locator.spec.js +89 -0
  62. package/dist/tests/regex.spec.d.ts +2 -0
  63. package/dist/tests/regex.spec.d.ts.map +1 -0
  64. package/dist/tests/regex.spec.js +19 -0
  65. package/dist/tests/vitest.config.d.mts +3 -0
  66. package/dist/tests/vitest.config.d.mts.map +1 -0
  67. package/dist/tests/vitest.config.mjs +6 -0
  68. package/dist/types/errors.d.ts +7 -0
  69. package/dist/types/errors.d.ts.map +1 -0
  70. package/dist/types/errors.js +15 -0
  71. package/dist/types/index.d.ts +234 -0
  72. package/dist/types/index.d.ts.map +1 -0
  73. package/dist/types/index.js +22 -0
  74. package/dist/utils.d.ts +8 -0
  75. package/dist/utils.d.ts.map +1 -0
  76. package/dist/utils.js +66 -0
  77. package/dist/vision/index.d.ts +64 -0
  78. package/dist/vision/index.d.ts.map +1 -0
  79. package/dist/vision/index.js +106 -0
  80. package/package.json +63 -0
  81. package/tsconfig.json +15 -0
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EmulatorProvider = void 0;
4
+ const types_1 = require("../../types");
5
+ const device_1 = require("../../device");
6
+ const appium_1 = require("../appium");
7
+ const utils_1 = require("../../utils");
8
+ const logger_1 = require("../../logger");
9
+ class EmulatorProvider {
10
+ project;
11
+ sessionId;
12
+ constructor(project, appBundleId) {
13
+ this.project = project;
14
+ if (appBundleId) {
15
+ logger_1.logger.log(`Bundle id is specified (${appBundleId}) but ignored for Emulator provider.`);
16
+ }
17
+ }
18
+ async getDevice() {
19
+ return await this.createDriver();
20
+ }
21
+ async globalSetup() {
22
+ (0, utils_1.validateBuildPath)(this.project.use.buildPath, this.project.use.platform == types_1.Platform.ANDROID ? ".apk" : ".app");
23
+ if (this.project.use.platform == types_1.Platform.ANDROID) {
24
+ const androidHome = process.env.ANDROID_HOME;
25
+ const androidSimulatorConfigDocLink = "https://github.com/empirical-run/appwright/blob/main/docs/config.md#android-emulator";
26
+ if (!androidHome) {
27
+ throw new Error(`The ANDROID_HOME environment variable is not set.
28
+ This variable is required to locate your Android SDK.
29
+ Please set it to the correct path of your Android SDK installation.
30
+ Follow the steps mentioned in ${androidSimulatorConfigDocLink} to run test on Android emulator.`);
31
+ }
32
+ const javaHome = process.env.JAVA_HOME;
33
+ if (!javaHome) {
34
+ throw new Error(`The JAVA_HOME environment variable is not set.
35
+ Follow the steps mentioned in ${androidSimulatorConfigDocLink} to run test on Android emulator.`);
36
+ }
37
+ await (0, appium_1.isEmulatorInstalled)(this.project.use.platform);
38
+ }
39
+ }
40
+ async createDriver() {
41
+ await (0, appium_1.installDriver)(this.project.use.platform == types_1.Platform.ANDROID
42
+ ? "uiautomator2"
43
+ : "xcuitest");
44
+ await (0, appium_1.startAppiumServer)(this.project.use.device?.provider);
45
+ const WebDriver = (await import("webdriver")).default;
46
+ const webDriverClient = await WebDriver.newSession(await this.createConfig());
47
+ this.sessionId = webDriverClient.sessionId;
48
+ const expectTimeout = this.project.use.expectTimeout;
49
+ const testOptions = {
50
+ expectTimeout,
51
+ };
52
+ return new device_1.Device(webDriverClient, undefined, testOptions, this.project.use.device?.provider);
53
+ }
54
+ async createConfig() {
55
+ const platformName = this.project.use.platform;
56
+ const udid = this.project.use.device.udid;
57
+ let appPackageName;
58
+ let appLaunchableActivity;
59
+ if (platformName == types_1.Platform.ANDROID) {
60
+ const { packageName, launchableActivity } = await (0, appium_1.getApkDetails)(this.project.use.buildPath);
61
+ appPackageName = packageName;
62
+ appLaunchableActivity = launchableActivity;
63
+ }
64
+ return {
65
+ port: 4723,
66
+ capabilities: {
67
+ "appium:deviceName": this.project.use.device?.name,
68
+ "appium:udid": udid,
69
+ "appium:automationName": platformName == types_1.Platform.ANDROID ? "uiautomator2" : "xcuitest",
70
+ "appium:platformVersion": this.project.use.device
71
+ .osVersion,
72
+ "appium:appActivity": appLaunchableActivity,
73
+ "appium:appPackage": appPackageName,
74
+ platformName: platformName,
75
+ "appium:autoGrantPermissions": true,
76
+ "appium:app": this.project.use.buildPath,
77
+ "appium:autoAcceptAlerts": true,
78
+ "appium:fullReset": true,
79
+ "appium:deviceOrientation": this.project.use.device?.orientation,
80
+ "appium:settings[snapshotMaxDepth]": 62,
81
+ "appium:wdaLaunchTimeout": 300_000,
82
+ },
83
+ };
84
+ }
85
+ }
86
+ exports.EmulatorProvider = EmulatorProvider;
@@ -0,0 +1,5 @@
1
+ import { AppwrightConfig, DeviceProvider } from "../types";
2
+ import { FullProject } from "@playwright/test";
3
+ export declare function getProviderClass(provider: string): any;
4
+ export declare function createDeviceProvider(project: FullProject<AppwrightConfig>): DeviceProvider;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAatD;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,GACpC,cAAc,CAQhB"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProviderClass = getProviderClass;
4
+ exports.createDeviceProvider = createDeviceProvider;
5
+ const browserstack_1 = require("./browserstack");
6
+ const local_1 = require("./local");
7
+ const emulator_1 = require("./emulator");
8
+ const lambdatest_1 = require("./lambdatest");
9
+ function getProviderClass(provider) {
10
+ switch (provider) {
11
+ case "browserstack":
12
+ return browserstack_1.BrowserStackDeviceProvider;
13
+ case "lambdatest":
14
+ return lambdatest_1.LambdaTestDeviceProvider;
15
+ case "emulator":
16
+ return emulator_1.EmulatorProvider;
17
+ case "local-device":
18
+ return local_1.LocalDeviceProvider;
19
+ default:
20
+ throw new Error(`Unknown device provider: ${provider}`);
21
+ }
22
+ }
23
+ function createDeviceProvider(project) {
24
+ const provider = project.use.device?.provider;
25
+ const appBundleId = project.use.appBundleId;
26
+ if (!provider) {
27
+ throw new Error("Device provider is not specified in the configuration.");
28
+ }
29
+ const ProviderClass = getProviderClass(provider);
30
+ return new ProviderClass(project, appBundleId);
31
+ }
@@ -0,0 +1,27 @@
1
+ import { AppwrightConfig, DeviceProvider } from "../../types";
2
+ import { FullProject } from "@playwright/test";
3
+ import { Device } from "../../device";
4
+ export declare class LambdaTestDeviceProvider implements DeviceProvider {
5
+ private project;
6
+ private appBundleId;
7
+ private sessionDetails?;
8
+ sessionId?: string;
9
+ private projectName;
10
+ constructor(project: FullProject<AppwrightConfig>, appBundleId: string | undefined);
11
+ globalSetup(): Promise<void>;
12
+ getDevice(): Promise<Device>;
13
+ private validateConfig;
14
+ private createDriver;
15
+ static downloadVideo(sessionId: string, outputDir: string, fileName: string): Promise<{
16
+ path: string;
17
+ contentType: string;
18
+ } | null>;
19
+ syncTestDetails(details: {
20
+ status?: string;
21
+ reason?: string;
22
+ name?: string;
23
+ }): Promise<any>;
24
+ private deviceInfoForSession;
25
+ private createConfig;
26
+ }
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/lambdatest/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAwDtC,qBAAa,wBAAyB,YAAW,cAAc;IAM3D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;IANrB,OAAO,CAAC,cAAc,CAAC,CAA2B;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,WAAW,CAAgC;gBAGzC,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,EACrC,WAAW,EAAE,MAAM,GAAG,SAAS;IASnC,WAAW;IA8DX,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAMlC,OAAO,CAAC,cAAc;YASR,YAAY;WAeb,aAAa,CACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IA6ElD,eAAe,CAAC,OAAO,EAAE;QAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf;IA0BD,OAAO,CAAC,oBAAoB;IAuB5B,OAAO,CAAC,YAAY;CA6CrB"}
@@ -0,0 +1,280 @@
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.LambdaTestDeviceProvider = void 0;
7
+ const async_retry_1 = __importDefault(require("async-retry"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const form_data_1 = __importDefault(require("form-data"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const device_1 = require("../../device");
12
+ const logger_1 = require("../../logger");
13
+ const utils_1 = require("./utils");
14
+ const browserStackToLambdaTest = {
15
+ deviceName: {
16
+ "Google Pixel 8": "Pixel 8",
17
+ },
18
+ osVersion: {
19
+ "14.0": "14",
20
+ },
21
+ };
22
+ const API_BASE_URL = "https://mobile-api.lambdatest.com/mobile-automation/api/v1";
23
+ const envVarKeyForBuild = (projectName) => `LAMBDATEST_APP_URL_${projectName.toUpperCase()}`;
24
+ async function getSessionDetails(sessionId) {
25
+ const response = await fetch(`${API_BASE_URL}/sessions/${sessionId}`, {
26
+ method: "GET",
27
+ headers: {
28
+ Authorization: (0, utils_1.getAuthHeader)(),
29
+ },
30
+ });
31
+ if (!response.ok) {
32
+ throw new Error(`Error fetching session details: ${response.statusText}`);
33
+ }
34
+ const data = await response.json();
35
+ return data;
36
+ }
37
+ class LambdaTestDeviceProvider {
38
+ project;
39
+ appBundleId;
40
+ sessionDetails;
41
+ sessionId;
42
+ projectName = path_1.default.basename(process.cwd());
43
+ constructor(project, appBundleId) {
44
+ this.project = project;
45
+ this.appBundleId = appBundleId;
46
+ if (!appBundleId) {
47
+ throw new Error("App Bundle ID is required for running tests on LambdaTest. Set the `appBundleId` for your projects that run on this provider.");
48
+ }
49
+ }
50
+ async globalSetup() {
51
+ if (!this.project.use.buildPath) {
52
+ throw new Error(`Build path not found. Please set the build path in the config file.`);
53
+ }
54
+ if (!(process.env.LAMBDATEST_USERNAME && process.env.LAMBDATEST_ACCESS_KEY)) {
55
+ throw new Error("LAMBDATEST_USERNAME and LAMBDATEST_ACCESS_KEY are required environment variables for this device provider. Please set the LAMBDATEST_USERNAME and LAMBDATEST_ACCESS_KEY environment variables.");
56
+ }
57
+ const buildPath = this.project.use.buildPath;
58
+ const isHttpUrl = buildPath.startsWith("http");
59
+ const isLambdaTestUrl = buildPath.startsWith("lt://");
60
+ let appUrl = undefined;
61
+ if (isLambdaTestUrl) {
62
+ appUrl = buildPath;
63
+ }
64
+ else {
65
+ let body;
66
+ let headers = {
67
+ Authorization: (0, utils_1.getAuthHeader)(),
68
+ };
69
+ if (isHttpUrl) {
70
+ body = new URLSearchParams({
71
+ url: buildPath,
72
+ visibility: "team",
73
+ storage: "url",
74
+ name: this.projectName,
75
+ });
76
+ }
77
+ else {
78
+ if (!fs_1.default.existsSync(buildPath)) {
79
+ throw new Error(`Build file not found: ${buildPath}`);
80
+ }
81
+ const form = new form_data_1.default();
82
+ form.append("visibility", "team");
83
+ form.append("storage", "file");
84
+ form.append("appFile", fs_1.default.createReadStream(buildPath));
85
+ form.append("name", this.projectName);
86
+ headers = { ...headers, ...form.getHeaders() };
87
+ body = form;
88
+ }
89
+ logger_1.logger.log(`Uploading: ${buildPath}`);
90
+ const fetch = (await import("node-fetch")).default;
91
+ const response = await fetch(`https://manual-api.lambdatest.com/app/upload/realDevice`, {
92
+ method: "POST",
93
+ headers,
94
+ body,
95
+ });
96
+ const data = await response.json();
97
+ appUrl = data.app_url;
98
+ if (!appUrl) {
99
+ logger_1.logger.error("Uploading the build failed:", data);
100
+ }
101
+ }
102
+ process.env[envVarKeyForBuild(this.project.name)] = appUrl;
103
+ }
104
+ async getDevice() {
105
+ this.validateConfig();
106
+ const config = this.createConfig();
107
+ return await this.createDriver(config);
108
+ }
109
+ validateConfig() {
110
+ const device = this.project.use.device;
111
+ if (!device.name || !device.osVersion) {
112
+ throw new Error("Device name and osVersion are required for running tests on LambdaTest. Please set the device name and osVersion in the `appwright.config.ts` file.");
113
+ }
114
+ }
115
+ async createDriver(config) {
116
+ const WebDriver = (await import("webdriver")).default;
117
+ const webDriverClient = await WebDriver.newSession(config);
118
+ this.sessionId = webDriverClient.sessionId;
119
+ const testOptions = {
120
+ expectTimeout: this.project.use.expectTimeout,
121
+ };
122
+ return new device_1.Device(webDriverClient, this.appBundleId, testOptions, this.project.use.device?.provider);
123
+ }
124
+ static async downloadVideo(sessionId, outputDir, fileName) {
125
+ const sessionData = await getSessionDetails(sessionId);
126
+ const sessionDetails = sessionData?.data;
127
+ const videoURL = sessionDetails?.video_url;
128
+ const pathToTestVideo = path_1.default.join(outputDir, `${fileName}.mp4`);
129
+ const tempPathForWriting = `${pathToTestVideo}.part`;
130
+ const dir = path_1.default.dirname(pathToTestVideo);
131
+ fs_1.default.mkdirSync(dir, { recursive: true });
132
+ const fileStream = fs_1.default.createWriteStream(tempPathForWriting);
133
+ //To catch the lambdatest error in case all retries fails
134
+ try {
135
+ if (videoURL) {
136
+ await (0, async_retry_1.default)(async () => {
137
+ const response = await fetch(videoURL, {
138
+ method: "GET",
139
+ });
140
+ if (response.status !== 200) {
141
+ // Retry if not 200
142
+ throw new Error(`Video not found: ${response.status} (URL: ${videoURL})`);
143
+ }
144
+ const reader = response.body?.getReader();
145
+ if (!reader) {
146
+ throw new Error("Failed to get reader from response body.");
147
+ }
148
+ const streamToFile = async () => {
149
+ // eslint-disable-next-line no-constant-condition
150
+ while (true) {
151
+ const { done, value } = await reader.read();
152
+ if (done)
153
+ break;
154
+ fileStream.write(value);
155
+ }
156
+ };
157
+ await streamToFile();
158
+ fileStream.close();
159
+ }, {
160
+ retries: 10,
161
+ minTimeout: 3_000,
162
+ onRetry: (err, i) => {
163
+ if (i > 5) {
164
+ logger_1.logger.warn(`Retry attempt ${i} failed: ${err.message}`);
165
+ }
166
+ },
167
+ });
168
+ return new Promise((resolve, reject) => {
169
+ // Ensure file stream is closed even in case of an error
170
+ fileStream.on("finish", () => {
171
+ try {
172
+ fs_1.default.renameSync(tempPathForWriting, pathToTestVideo);
173
+ logger_1.logger.log(`Download finished and file closed: ${pathToTestVideo}`);
174
+ resolve({ path: pathToTestVideo, contentType: "video/mp4" });
175
+ }
176
+ catch (err) {
177
+ logger_1.logger.error(`Failed to rename file: `, err);
178
+ reject(err);
179
+ }
180
+ });
181
+ fileStream.on("error", (err) => {
182
+ logger_1.logger.error(`Failed to write file: ${err.message}`);
183
+ reject(err);
184
+ });
185
+ });
186
+ }
187
+ else {
188
+ return null;
189
+ }
190
+ }
191
+ catch (e) {
192
+ logger_1.logger.log(`Error Downloading video: `, e);
193
+ return null;
194
+ }
195
+ }
196
+ async syncTestDetails(details) {
197
+ const response = await fetch(`${API_BASE_URL}/sessions/${this.sessionId}`, {
198
+ method: "PATCH",
199
+ headers: {
200
+ Authorization: (0, utils_1.getAuthHeader)(),
201
+ "Content-Type": "application/json",
202
+ },
203
+ body: details.status
204
+ ? JSON.stringify({
205
+ name: details.name,
206
+ status_ind: details.status,
207
+ custom_data: details.reason,
208
+ })
209
+ : JSON.stringify({
210
+ name: details.name,
211
+ }),
212
+ });
213
+ if (!response.ok) {
214
+ //TODO: Check whether add retry here or leave it as is because while setting the name of test
215
+ //sometimes the session is not getting created till then thus this fails.
216
+ // throw new Error(`Error setting session details: ${response.statusText}`);
217
+ }
218
+ const responseData = await response.json();
219
+ return responseData;
220
+ }
221
+ deviceInfoForSession() {
222
+ let deviceName = this.project.use.device?.name;
223
+ let osVersion = this.project.use.device.osVersion;
224
+ if (deviceName &&
225
+ Object.keys(browserStackToLambdaTest.deviceName).includes(deviceName)) {
226
+ // we map BrowserStack names to LambdaTest for better usability
227
+ deviceName = browserStackToLambdaTest.deviceName[deviceName];
228
+ }
229
+ if (osVersion &&
230
+ Object.keys(browserStackToLambdaTest.osVersion).includes(osVersion)) {
231
+ osVersion = browserStackToLambdaTest.osVersion[osVersion];
232
+ }
233
+ return {
234
+ deviceName,
235
+ platformVersion: osVersion,
236
+ deviceOrientation: this.project.use.device?.orientation,
237
+ };
238
+ }
239
+ createConfig() {
240
+ const platformName = this.project.use.platform;
241
+ const envVarKey = envVarKeyForBuild(this.project.name);
242
+ const deviceConfig = this.project.use.device;
243
+ const configuredAppiumVersion = deviceConfig.appiumVersion ??
244
+ process.env.LAMBDATEST_APPIUM_VERSION ??
245
+ "3.1.0";
246
+ if (!process.env[envVarKey]) {
247
+ throw new Error(`process.env.${envVarKey} is not set. Did the file upload work?`);
248
+ }
249
+ return {
250
+ port: 443,
251
+ protocol: "https",
252
+ path: "/wd/hub",
253
+ logLevel: "warn",
254
+ user: process.env.LAMBDATEST_USERNAME,
255
+ key: process.env.LAMBDATEST_ACCESS_KEY,
256
+ hostname: "mobile-hub.lambdatest.com",
257
+ capabilities: {
258
+ ...this.deviceInfoForSession(),
259
+ appiumVersion: configuredAppiumVersion,
260
+ platformName: platformName,
261
+ queueTimeout: 600,
262
+ idleTimeout: 600,
263
+ app: process.env[envVarKey],
264
+ devicelog: true,
265
+ video: true,
266
+ build: `${this.projectName} ${platformName} ${process.env.GITHUB_ACTIONS === "true"
267
+ ? `CI ${process.env.GITHUB_RUN_ID}`
268
+ : process.env.USER}`,
269
+ project: this.projectName,
270
+ autoGrantPermissions: true,
271
+ autoAcceptAlerts: true,
272
+ isRealMobile: true,
273
+ enableImageInjection: this.project.use.device
274
+ ?.enableCameraImageInjection,
275
+ "settings[snapshotMaxDepth]": 62,
276
+ },
277
+ };
278
+ }
279
+ }
280
+ exports.LambdaTestDeviceProvider = LambdaTestDeviceProvider;
@@ -0,0 +1,3 @@
1
+ export declare function getAuthHeader(): string;
2
+ export declare function uploadImageToLambdaTest(imagePath: string): Promise<string>;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/providers/lambdatest/utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,WAK5B;AAMD,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CA0BjB"}
@@ -0,0 +1,36 @@
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.getAuthHeader = getAuthHeader;
7
+ exports.uploadImageToLambdaTest = uploadImageToLambdaTest;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const form_data_1 = __importDefault(require("form-data"));
10
+ function getAuthHeader() {
11
+ const userName = process.env.LAMBDATEST_USERNAME;
12
+ const accessKey = process.env.LAMBDATEST_ACCESS_KEY;
13
+ const key = Buffer.from(`${userName}:${accessKey}`).toString("base64");
14
+ return `Basic ${key}`;
15
+ }
16
+ async function uploadImageToLambdaTest(imagePath) {
17
+ const formData = new form_data_1.default();
18
+ if (!fs_1.default.existsSync(imagePath)) {
19
+ throw new Error(`No image file found at the specified path: ${imagePath}. Please provide a valid image file.
20
+ Supported formats include JPG, JPEG, and PNG. Ensure the file exists and the path is correct.`);
21
+ }
22
+ formData.append("media_file", fs_1.default.createReadStream(imagePath));
23
+ formData.append("type", "image");
24
+ formData.append("custom_id", "SampleMedia");
25
+ const fetch = (await import("node-fetch")).default;
26
+ const response = await fetch("https://mobile-mgm.lambdatest.com/mfs/v1.0/media/upload", {
27
+ method: "POST",
28
+ headers: {
29
+ Authorization: getAuthHeader(),
30
+ },
31
+ body: formData,
32
+ });
33
+ const data = (await response.json());
34
+ const imageURL = data.media_url.trim();
35
+ return imageURL;
36
+ }
@@ -0,0 +1,13 @@
1
+ import { AppwrightConfig, DeviceProvider } from "../../types";
2
+ import { Device } from "../../device";
3
+ import { FullProject } from "@playwright/test";
4
+ export declare class LocalDeviceProvider implements DeviceProvider {
5
+ private project;
6
+ sessionId?: string;
7
+ constructor(project: FullProject<AppwrightConfig>, appBundleId: string | undefined);
8
+ getDevice(): Promise<Device>;
9
+ globalSetup(): Promise<undefined>;
10
+ private createDriver;
11
+ private createConfig;
12
+ }
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/local/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,cAAc,EAIf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAY/C,qBAAa,mBAAoB,YAAW,cAAc;IAItD,OAAO,CAAC,OAAO;IAHjB,SAAS,CAAC,EAAE,MAAM,CAAC;gBAGT,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,EAC7C,WAAW,EAAE,MAAM,GAAG,SAAS;IAS3B,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAI5B,WAAW;YAgBH,YAAY;YAyBZ,YAAY;CA6C3B"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalDeviceProvider = void 0;
4
+ const types_1 = require("../../types");
5
+ const device_1 = require("../../device");
6
+ const appium_1 = require("../appium");
7
+ const utils_1 = require("../../utils");
8
+ const logger_1 = require("../../logger");
9
+ class LocalDeviceProvider {
10
+ project;
11
+ sessionId;
12
+ constructor(project, appBundleId) {
13
+ this.project = project;
14
+ if (appBundleId) {
15
+ logger_1.logger.log(`Bundle id is specified (${appBundleId}) but ignored for local device provider.`);
16
+ }
17
+ }
18
+ async getDevice() {
19
+ return await this.createDriver();
20
+ }
21
+ async globalSetup() {
22
+ (0, utils_1.validateBuildPath)(this.project.use.buildPath, this.project.use.platform == types_1.Platform.ANDROID ? ".apk" : ".ipa");
23
+ if (this.project.use.platform == types_1.Platform.ANDROID) {
24
+ const androidHome = process.env.ANDROID_HOME;
25
+ if (!androidHome) {
26
+ return Promise.reject("The ANDROID_HOME environment variable is not set. This variable is required to locate your Android SDK. Please set it to the correct path of your Android SDK installation. For detailed instructions on how to set up the Android SDK path, visit: https://developer.android.com/tools");
27
+ }
28
+ }
29
+ }
30
+ async createDriver() {
31
+ await (0, appium_1.installDriver)(this.project.use.platform == types_1.Platform.ANDROID
32
+ ? "uiautomator2"
33
+ : "xcuitest");
34
+ await (0, appium_1.startAppiumServer)(this.project.use.device?.provider);
35
+ const WebDriver = (await import("webdriver")).default;
36
+ const webDriverClient = await WebDriver.newSession(await this.createConfig());
37
+ this.sessionId = webDriverClient.sessionId;
38
+ const bundleId = await (0, appium_1.getAppBundleId)(this.project.use.buildPath);
39
+ const expectTimeout = this.project.use.expectTimeout;
40
+ const testOptions = {
41
+ expectTimeout,
42
+ };
43
+ return new device_1.Device(webDriverClient, bundleId, testOptions, this.project.use.device?.provider);
44
+ }
45
+ async createConfig() {
46
+ const platformName = this.project.use.platform;
47
+ let appPackageName;
48
+ let appLaunchableActivity;
49
+ if (platformName == types_1.Platform.ANDROID) {
50
+ const { packageName, launchableActivity } = await (0, appium_1.getApkDetails)(this.project.use.buildPath);
51
+ appPackageName = packageName;
52
+ appLaunchableActivity = launchableActivity;
53
+ }
54
+ let udid = this.project.use.device.udid;
55
+ if (!udid) {
56
+ if (platformName == types_1.Platform.IOS) {
57
+ udid = await (0, appium_1.getConnectedIOSDeviceUDID)();
58
+ }
59
+ else {
60
+ const activeAndroidDevices = await (0, appium_1.getActiveAndroidDevices)();
61
+ if (activeAndroidDevices > 1) {
62
+ logger_1.logger.warn(`Multiple active devices detected. Selecting one for the test.
63
+ To specify a device, use the udid property. Run "adb devices" to get the UDID for active devices.`);
64
+ }
65
+ }
66
+ }
67
+ return {
68
+ port: 4723,
69
+ capabilities: {
70
+ "appium:deviceName": this.project.use.device?.name,
71
+ "appium:udid": udid,
72
+ "appium:automationName": platformName == types_1.Platform.ANDROID ? "uiautomator2" : "xcuitest",
73
+ platformName: platformName,
74
+ "appium:autoGrantPermissions": true,
75
+ "appium:app": this.project.use.buildPath,
76
+ "appium:appActivity": appLaunchableActivity,
77
+ "appium:appPackage": appPackageName,
78
+ "appium:autoAcceptAlerts": true,
79
+ "appium:fullReset": true,
80
+ "appium:deviceOrientation": this.project.use.device?.orientation,
81
+ "appium:settings[snapshotMaxDepth]": 62,
82
+ },
83
+ };
84
+ }
85
+ }
86
+ exports.LocalDeviceProvider = LocalDeviceProvider;
@@ -0,0 +1,13 @@
1
+ import type { Reporter, TestCase, TestResult } from "@playwright/test/reporter";
2
+ declare class VideoDownloader implements Reporter {
3
+ private downloadPromises;
4
+ onBegin(): void;
5
+ onTestBegin(test: TestCase, result: TestResult): void;
6
+ onTestEnd(test: TestCase, result: TestResult): void;
7
+ onEnd(): Promise<void>;
8
+ private trimAndAttachPersistentDeviceVideo;
9
+ private downloadAndAttachDeviceVideo;
10
+ private providerSupportsVideo;
11
+ }
12
+ export default VideoDownloader;
13
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAUhF,cAAM,eAAgB,YAAW,QAAQ;IACvC,OAAO,CAAC,gBAAgB,CAAsB;IAE9C,OAAO;IAQP,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IAU9C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU;IA0FtC,KAAK;YAKG,kCAAkC;IA4ChD,OAAO,CAAC,4BAA4B;IAyBpC,OAAO,CAAC,qBAAqB;CAI9B;AA8ED,eAAe,eAAe,CAAC"}