Skip to content

Commit e88da45

Browse files
Refactor: Enhance startSession function with improved device filtering and validation
1 parent c2f0670 commit e88da45

File tree

1 file changed

+126
-27
lines changed

1 file changed

+126
-27
lines changed

src/tools/applive-utils/start-session.ts

Lines changed: 126 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ interface StartSessionArgs {
2222

2323
/**
2424
* Starts an App Live session after filtering, fuzzy matching, and launching.
25+
* @param args - The arguments for starting the session.
26+
* @returns The launch URL for the session.
27+
* @throws Will throw an error if no devices are found or if the app URL is invalid.
2528
*/
2629
export async function startSession(args: StartSessionArgs): Promise<string> {
2730
const { appPath, desiredPlatform, desiredPhone } = args;
@@ -32,52 +35,126 @@ export async function startSession(args: StartSessionArgs): Promise<string> {
3235
group.devices.map((dev: any) => ({ ...dev, os: group.os })),
3336
);
3437

35-
// Exact filter by platform and version
38+
desiredPlatformVersion = resolvePlatformVersion(
39+
allDevices,
40+
desiredPlatform,
41+
desiredPlatformVersion,
42+
);
43+
44+
const filteredDevices = filterDevicesByPlatformAndVersion(
45+
allDevices,
46+
desiredPlatform,
47+
desiredPlatformVersion,
48+
);
49+
50+
const matches = await fuzzySearchDevices(filteredDevices, desiredPhone);
51+
52+
const selectedDevice = validateAndSelectDevice(
53+
matches,
54+
desiredPhone,
55+
desiredPlatform,
56+
desiredPlatformVersion,
57+
);
58+
59+
const { app_url } = await uploadApp(appPath);
60+
61+
validateAppUrl(app_url);
62+
63+
const launchUrl = constructLaunchUrl(
64+
app_url,
65+
selectedDevice,
66+
desiredPlatform,
67+
desiredPlatformVersion,
68+
);
69+
70+
openBrowser(launchUrl);
71+
72+
return launchUrl;
73+
}
74+
75+
/**
76+
* Resolves the platform version based on the desired platform and version.
77+
* @param allDevices - The list of all devices.
78+
* @param desiredPlatform - The desired platform (android or ios).
79+
* @param desiredPlatformVersion - The desired platform version.
80+
* @returns The resolved platform version.
81+
* @throws Will throw an error if the platform version is not valid.
82+
*/
83+
function resolvePlatformVersion(
84+
allDevices: DeviceEntry[],
85+
desiredPlatform: string,
86+
desiredPlatformVersion: string,
87+
): string {
3688
if (
37-
desiredPlatformVersion == "latest" ||
38-
desiredPlatformVersion == "oldest"
89+
desiredPlatformVersion === "latest" ||
90+
desiredPlatformVersion === "oldest"
3991
) {
4092
const filtered = allDevices.filter((d) => d.os === desiredPlatform);
4193
filtered.sort((a, b) => {
4294
const versionA = parseFloat(a.os_version);
4395
const versionB = parseFloat(b.os_version);
4496
return desiredPlatformVersion === "latest"
45-
? versionB - versionA // descending for "latest"
46-
: versionA - versionB; // ascending for specific version
97+
? versionB - versionA
98+
: versionA - versionB;
4799
});
48100

49-
const requiredVersion = filtered[0].os_version;
50-
51-
desiredPlatformVersion = requiredVersion;
101+
return filtered[0].os_version;
52102
}
53-
const filtered = allDevices.filter((d) => {
103+
return desiredPlatformVersion;
104+
}
105+
106+
/**
107+
* Filters devices based on the desired platform and version.
108+
* @param allDevices - The list of all devices.
109+
* @param desiredPlatform - The desired platform (android or ios).
110+
* @param desiredPlatformVersion - The desired platform version.
111+
* @returns The filtered list of devices.
112+
* @throws Will throw an error if the platform version is not valid.
113+
*/
114+
function filterDevicesByPlatformAndVersion(
115+
allDevices: DeviceEntry[],
116+
desiredPlatform: string,
117+
desiredPlatformVersion: string,
118+
): DeviceEntry[] {
119+
return allDevices.filter((d) => {
54120
if (d.os !== desiredPlatform) return false;
55121

56-
// Attempt to compare as floats
57122
try {
58123
const versionA = parseFloat(d.os_version);
59124
const versionB = parseFloat(desiredPlatformVersion);
60125
return versionA === versionB;
61126
} catch {
62-
// Fallback to exact string match if parsing fails
63127
return d.os_version === desiredPlatformVersion;
64128
}
65129
});
130+
}
66131

67-
// Fuzzy match
68-
const matches = await fuzzySearchDevices(filtered, desiredPhone);
69-
132+
/**
133+
* Validates the selected device and handles multiple matches.
134+
* @param matches - The list of device matches.
135+
* @param desiredPhone - The desired phone name.
136+
* @param desiredPlatform - The desired platform (android or ios).
137+
* @param desiredPlatformVersion - The desired platform version.
138+
* @returns The selected device entry.
139+
*/
140+
function validateAndSelectDevice(
141+
matches: DeviceEntry[],
142+
desiredPhone: string,
143+
desiredPlatform: string,
144+
desiredPlatformVersion: string,
145+
): DeviceEntry {
70146
if (matches.length === 0) {
71147
throw new Error(
72148
`No devices found matching "${desiredPhone}" for ${desiredPlatform} ${desiredPlatformVersion}`,
73149
);
74150
}
151+
75152
const exactMatch = matches.find(
76153
(d) => d.display_name.toLowerCase() === desiredPhone.toLowerCase(),
77154
);
78155

79156
if (exactMatch) {
80-
matches.splice(0, matches.length, exactMatch); // Replace matches with the exact match
157+
return exactMatch;
81158
} else if (matches.length >= 1) {
82159
const names = matches.map((d) => d.display_name).join(", ");
83160
const error_message =
@@ -87,57 +164,79 @@ export async function startSession(args: StartSessionArgs): Promise<string> {
87164
throw new Error(`${error_message}`);
88165
}
89166

90-
const { app_url } = await uploadApp(appPath);
167+
return matches[0];
168+
}
91169

92-
if (!app_url.match("bs://")) {
170+
/**
171+
* Validates the app URL.
172+
* @param appUrl - The app URL to validate.
173+
* @throws Will throw an error if the app URL is not valid.
174+
*/
175+
function validateAppUrl(appUrl: string): void {
176+
if (!appUrl.match("bs://")) {
93177
throw new Error("The app path is not a valid BrowserStack app URL.");
94178
}
179+
}
95180

96-
const device = matches[0];
181+
/**
182+
* Constructs the launch URL for the App Live session.
183+
* @param appUrl - The app URL.
184+
* @param device - The selected device entry.
185+
* @param desiredPlatform - The desired platform (android or ios).
186+
* @param desiredPlatformVersion - The desired platform version.
187+
* @returns The constructed launch URL.
188+
*/
189+
function constructLaunchUrl(
190+
appUrl: string,
191+
device: DeviceEntry,
192+
desiredPlatform: string,
193+
desiredPlatformVersion: string,
194+
): string {
97195
const deviceParam = sanitizeUrlParam(
98196
device.display_name.replace(/\s+/g, "+"),
99197
);
100198

101199
const params = new URLSearchParams({
102200
os: desiredPlatform,
103201
os_version: desiredPlatformVersion,
104-
app_hashed_id: app_url.split("bs://").pop() || "",
202+
app_hashed_id: appUrl.split("bs://").pop() || "",
105203
scale_to_fit: "true",
106204
speed: "1",
107205
start: "true",
108206
});
109-
const launchUrl = `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
110207

208+
return `https://app-live.browserstack.com/dashboard#${params.toString()}&device=${deviceParam}`;
209+
}
210+
211+
/**
212+
* Opens the launch URL in the default browser.
213+
* @param launchUrl - The URL to open.
214+
* @throws Will throw an error if the browser fails to open.
215+
*/
216+
function openBrowser(launchUrl: string): void {
111217
try {
112-
// Use platform-specific commands with proper escaping
113218
const command =
114219
process.platform === "darwin"
115220
? ["open", launchUrl]
116221
: process.platform === "win32"
117222
? ["cmd", "/c", "start", launchUrl]
118223
: ["xdg-open", launchUrl];
119224

120-
// nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
121225
const child = childProcess.spawn(command[0], command.slice(1), {
122226
stdio: "ignore",
123227
detached: true,
124228
});
125229

126-
// Handle process errors
127230
child.on("error", (error) => {
128231
logger.error(
129232
`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
130233
);
131234
});
132235

133-
// Unref the child process to allow the parent to exit
134236
child.unref();
135-
136-
return launchUrl;
137237
} catch (error) {
138238
logger.error(
139239
`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
140240
);
141-
return launchUrl;
142241
}
143242
}

0 commit comments

Comments
 (0)