-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathappautomate.ts
More file actions
157 lines (136 loc) · 4.01 KB
/
Copy pathappautomate.ts
File metadata and controls
157 lines (136 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import fs from "fs";
import axios from "axios";
import config from "../../config.js";
import FormData from "form-data";
import { customFuzzySearch } from "../../lib/fuzzy.js";
import { DOMAINS } from "../../lib/domains.js";
interface Device {
device: string;
display_name: string;
os_version: string;
real_mobile: boolean;
}
interface UploadResponse {
app_url: string;
custom_id?: string;
shareable_id?: string;
}
/**
* Finds devices that exactly match the provided display name.
* Uses fuzzy search first, and then filters for exact case-insensitive match.
*/
export function findMatchingDevice(
devices: Device[],
deviceName: string,
): Device[] {
const matches = customFuzzySearch(devices, ["display_name"], deviceName, 5);
if (matches.length === 0) {
const availableDevices = [
...new Set(devices.map((d) => d.display_name)),
].join(", ");
throw new Error(
`No devices found matching "${deviceName}". Available devices: ${availableDevices}`,
);
}
const exactMatches = matches.filter(
(m) => m.display_name.toLowerCase() === deviceName.toLowerCase(),
);
if (exactMatches.length === 0) {
const suggestions = [...new Set(matches.map((d) => d.display_name))].join(
", ",
);
throw new Error(
`Alternative devices found: ${suggestions}. Please select one of these exact device names.`,
);
}
return exactMatches;
}
/**
* Extracts all unique OS versions from a device list and sorts them.
*/
export function getDeviceVersions(devices: Device[]): string[] {
return [...new Set(devices.map((d) => d.os_version))].sort();
}
/**
* Resolves the requested platform version against available versions.
* Supports 'latest' and 'oldest' as dynamic selectors.
*/
export function resolveVersion(
versions: string[],
requestedVersion: string,
): string {
if (requestedVersion === "latest") {
return versions[versions.length - 1];
}
if (requestedVersion === "oldest") {
return versions[0];
}
const match = versions.find((v) => v === requestedVersion);
if (!match) {
throw new Error(
`Version "${requestedVersion}" not found. Available versions: ${versions.join(", ")}`,
);
}
return match;
}
/**
* Validates the input arguments for taking app screenshots.
* Checks for presence and correctness of platform, device, and file types.
*/
export function validateArgs(args: {
desiredPlatform: string;
desiredPlatformVersion: string;
appPath: string;
desiredPhone: string;
}): void {
const { desiredPlatform, desiredPlatformVersion, appPath, desiredPhone } =
args;
if (!desiredPlatform || !desiredPhone) {
throw new Error(
"Missing required arguments: desiredPlatform and desiredPhone are required",
);
}
if (!desiredPlatformVersion) {
throw new Error(
"Missing required arguments: desiredPlatformVersion is required",
);
}
if (!appPath) {
throw new Error("You must provide an appPath.");
}
if (desiredPlatform === "android" && !appPath.endsWith(".apk")) {
throw new Error("You must provide a valid Android app path (.apk).");
}
if (desiredPlatform === "ios" && !appPath.endsWith(".ipa")) {
throw new Error("You must provide a valid iOS app path (.ipa).");
}
}
/**
* Uploads an application file to AppAutomate and returns the app URL
*/
export async function uploadApp(appPath: string): Promise<string> {
const filePath = appPath;
if (!fs.existsSync(filePath)) {
throw new Error(`File not found at path: ${filePath}`);
}
const formData = new FormData();
formData.append("file", fs.createReadStream(filePath));
const response = await axios.post<UploadResponse>(
`${DOMAINS.API_CLOUD}/app-automate/upload`,
formData,
{
headers: {
...formData.getHeaders(),
},
auth: {
username: config.browserstackUsername,
password: config.browserstackAccessKey,
},
},
);
if (response.data.app_url) {
return response.data.app_url;
} else {
throw new Error(`Failed to upload app: ${response.data}`);
}
}