-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathstart-session.ts
More file actions
118 lines (104 loc) · 3.54 KB
/
Copy pathstart-session.ts
File metadata and controls
118 lines (104 loc) · 3.54 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
import logger from "../../logger.js";
import childProcess from "child_process";
import {
getDevicesAndBrowsers,
BrowserStackProducts,
} from "../../lib/device-cache.js";
import { sanitizeUrlParam } from "../../lib/utils.js";
import { uploadApp } from "./upload-app.js";
import { findDeviceByName } from "./device-search.js";
import { pickVersion } from "./version-utils.js";
import { DeviceEntry } from "./types.js";
import { DOMAINS } from "../../lib/domains.js";
interface StartSessionArgs {
appPath: string;
desiredPlatform: "android" | "ios";
desiredPhone: string;
desiredPlatformVersion: string;
}
/**
* Start an App Live session: filter, select, upload, and open.
*/
export async function startSession(args: StartSessionArgs): Promise<string> {
const { appPath, desiredPlatform, desiredPhone, desiredPlatformVersion } =
args;
// 1) Fetch devices for APP_LIVE
const data = await getDevicesAndBrowsers(BrowserStackProducts.APP_LIVE);
const all: DeviceEntry[] = data.mobile.flatMap((grp: any) =>
grp.devices.map((dev: any) => ({ ...dev, os: grp.os })),
);
// 2) Filter by OS
const osMatches = all.filter((d) => d.os === desiredPlatform);
if (!osMatches.length) {
throw new Error(`No devices for OS "${desiredPlatform}"`);
}
// 3) Select by name
const nameMatches = findDeviceByName(osMatches, desiredPhone);
// 4) Resolve version
const versions = [...new Set(nameMatches.map((d) => d.os_version))];
const version = pickVersion(versions, desiredPlatformVersion);
// 5) Final candidates for version
const final = nameMatches.filter((d) => d.os_version === version);
if (!final.length) {
throw new Error(
`No devices for version "${version}" on ${desiredPlatform}`,
);
}
const selected = final[0];
let note = "";
if (
version != desiredPlatformVersion &&
desiredPlatformVersion !== "latest" &&
desiredPlatformVersion !== "oldest"
) {
note = `\n Note: The requested version "${desiredPlatformVersion}" is not available. Using "${version}" instead.`;
}
// 6) Upload app
const { app_url } = await uploadApp(appPath);
logger.info(`App uploaded: ${app_url}`);
// 7) Build URL & open
const deviceParam = sanitizeUrlParam(
selected.display_name.replace(/\s+/g, "+"),
);
const params = new URLSearchParams({
os: desiredPlatform,
os_version: version,
app_hashed_id: app_url.split("bs://").pop() || "",
scale_to_fit: "true",
speed: "1",
start: "true",
});
const launchUrl = `${DOMAINS.APP_LIVE}/dashboard#${params.toString()}&device=${deviceParam}`;
openBrowser(launchUrl);
return launchUrl + note;
}
/**
* Opens the launch URL in the default browser.
* @param launchUrl - The URL to open.
* @throws Will throw an error if the browser fails to open.
*/
function openBrowser(launchUrl: string): void {
try {
const command =
process.platform === "darwin"
? ["open", launchUrl]
: process.platform === "win32"
? ["cmd", "/c", "start", launchUrl]
: ["xdg-open", launchUrl];
// nosemgrep:javascript.lang.security.detect-child-process.detect-child-process
const child = childProcess.spawn(command[0], command.slice(1), {
stdio: "ignore",
detached: true,
});
child.on("error", (error) => {
logger.error(
`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
);
});
child.unref();
} catch (error) {
logger.error(
`Failed to open browser automatically: ${error}. Please open this URL manually: ${launchUrl}`,
);
}
}