-
Notifications
You must be signed in to change notification settings - Fork 29
refactor: update device handling in Automate tools #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…format and improve validation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR refactors device configuration from tuple-based arrays to object-based schemas across BrowserStack Automate and App Automate tools, improving type safety and API clarity.
Key Changes
- Introduced
MobileDeviceSchemaandDeviceSchemawith discriminated unions for platform-specific device configurations - Replaced tuple arrays with structured device objects in all schema definitions
- Added conversion utilities (
convertMobileDevicesToTuples) to bridge between new object format and existing tuple-based validators - Updated device descriptions to reflect object format usage
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tools/sdk-utils/common/schema.ts | Defines new MobileDeviceSchema and DeviceSchema with discriminated unions; updates RunTestsOnBrowserStackParamsShape to use object-based device arrays |
| src/tools/sdk-utils/common/device-validator.ts | Adds convertMobileDevicesToTuples utility and normalizes "mac" to "macos" for consistent platform handling |
| src/tools/sdk-utils/bstack/sdkHandler.ts | Implements object-to-tuple conversion logic with platform-specific default values for browser and browserVersion fields |
| src/tools/appautomate.ts | Integrates conversion utilities and applies default mobile device configuration when none provided |
| src/tools/appautomate-utils/native-execution/constants.ts | Updates schema to use MobileDeviceSchema with object format |
| src/tools/appautomate-utils/appium-sdk/handler.ts | Applies mobile device conversion and default device handling |
| src/tools/appautomate-utils/appium-sdk/constants.ts | Updates schema to use MobileDeviceSchema with object format |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/tools/sdk-utils/common/schema.ts
Outdated
| }), | ||
| z.object({ | ||
| platform: z.literal("android"), | ||
| deviceName: z.string().describe("Device name, e.g. 'Samsung Galaxy S24'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The device name description in the Android schema for DeviceSchema differs from the MobileDeviceSchema description (line 55). For consistency, consider using the same description: "Device name, e.g. 'Samsung Galaxy S24', 'Google Pixel 8'" to match the MobileDeviceSchema, which provides more examples.
| deviceName: z.string().describe("Device name, e.g. 'Samsung Galaxy S24'"), | |
| deviceName: z.string().describe("Device name, e.g. 'Samsung Galaxy S24', 'Google Pixel 8'"), |
src/tools/sdk-utils/common/schema.ts
Outdated
| }), | ||
| z.object({ | ||
| platform: z.literal("ios"), | ||
| deviceName: z.string().describe("Device name, e.g. 'iPhone 12 Pro'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The iOS device name example in DeviceSchema uses 'iPhone 12 Pro', while the MobileDeviceSchema uses 'iPhone 15', 'iPhone 14 Pro' (line 60). For consistency and to reflect more current devices, consider updating this to match the examples in MobileDeviceSchema.
| deviceName: z.string().describe("Device name, e.g. 'iPhone 12 Pro'"), | |
| deviceName: z.string().describe("Device name, e.g. 'iPhone 15', 'iPhone 14 Pro'"), |
| const normalizedDevices = devices.map((env) => { | ||
| const platform = (env[0] || "").toLowerCase(); | ||
| if (platform === "mac") { | ||
| return ["macos", ...env.slice(1)]; | ||
| } | ||
| return env; | ||
| }); |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "mac" to "macos" normalization is performed here for tuple-based validation. However, the object-based schema in DeviceSchema (line 85 of schema.ts) accepts both "mac" and "macos" via z.enum(["mac", "macos"]). This means the conversion from objects to tuples in sdkHandler.ts should also handle "mac" consistently. The current implementation does handle this (line 35), but this normalization logic creates redundancy. Consider documenting this behavior or refactoring to have a single source of truth for platform normalization.
| } | ||
| return env; | ||
| }); | ||
|
|
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace detected. Remove the trailing whitespace on this empty line for cleaner code formatting.
src/tools/sdk-utils/common/schema.ts
Outdated
| ), | ||
| }; | ||
|
|
||
|
|
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra blank line detected. Remove one of the consecutive empty lines for consistent code formatting.
src/tools/sdk-utils/common/schema.ts
Outdated
| platform: z.literal("windows"), | ||
| osVersion: z.string().describe("Windows version, e.g. '10', '11'"), | ||
| browser: z.string().describe("Browser name, e.g. 'chrome', 'firefox', 'edge'"), | ||
| browserVersion: z.string().describe("Browser version, e.g. '132', 'latest', 'oldest'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browserVersion field is required in the Windows and macOS schemas but is treated as optional in the conversion logic (line 33, 40). This inconsistency could cause runtime errors when devices are validated against the schema. Consider making browserVersion optional in the schema using .optional() to match the conversion behavior, or ensure it's always provided in the conversion.
src/tools/sdk-utils/common/schema.ts
Outdated
| platform: z.enum(["mac", "macos"]), | ||
| osVersion: z.string().describe("macOS version name, e.g. 'Sequoia', 'Ventura'"), | ||
| browser: z.string().describe("Browser name, e.g. 'safari', 'chrome'"), | ||
| browserVersion: z.string().describe("Browser version, e.g. 'latest'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browserVersion field is required in the macOS schema but is treated as optional in the conversion logic (line 40 in sdkHandler.ts uses device.browserVersion || "latest"). This inconsistency could cause runtime errors when devices are validated against the schema. Consider making browserVersion optional in the schema using .optional() to match the conversion behavior.
| browserVersion: z.string().describe("Browser version, e.g. 'latest'"), | |
| browserVersion: z.string().describe("Browser version, e.g. 'latest'").optional(), |
src/tools/sdk-utils/common/schema.ts
Outdated
| platform: z.literal("android"), | ||
| deviceName: z.string().describe("Device name, e.g. 'Samsung Galaxy S24'"), | ||
| osVersion: z.string().describe("Android version, e.g. '14', '16', 'latest'"), | ||
| browser: z.string().describe("Browser name, e.g. 'chrome', 'samsung browser'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browser field is required in the Android schema, but the conversion logic in sdkHandler.ts (line 47) treats it as optional with a default value (device.browser || "chrome"). This inconsistency could cause runtime errors when devices without a browser field are validated. Consider making browser optional in the schema using .optional() to match the conversion behavior.
| browser: z.string().describe("Browser name, e.g. 'chrome', 'samsung browser'"), | |
| browser: z.string().describe("Browser name, e.g. 'chrome', 'samsung browser'").optional(), |
src/tools/sdk-utils/common/schema.ts
Outdated
| platform: z.literal("ios"), | ||
| deviceName: z.string().describe("Device name, e.g. 'iPhone 12 Pro'"), | ||
| osVersion: z.string().describe("iOS version, e.g. '17', 'latest'"), | ||
| browser: z.string().describe("Browser name, typically 'safari'"), |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The browser field is required in the iOS schema, but the conversion logic in sdkHandler.ts (line 54) treats it as optional with a default value (device.browser || "safari"). This inconsistency could cause runtime errors when devices without a browser field are validated. Consider making browser optional in the schema using .optional() to match the conversion behavior.
| browser: z.string().describe("Browser name, typically 'safari'"), | |
| browser: z.string().describe("Browser name, typically 'safari'").optional(), |
| const tupleTargets: Array<Array<string>> = devices.map((device) => { | ||
| if (device.platform === "windows") { | ||
| return [ | ||
| "windows", | ||
| device.osVersion, | ||
| device.browser, | ||
| device.browserVersion || "latest", | ||
| ]; | ||
| } else if (device.platform === "mac" || device.platform === "macos") { | ||
| return [ | ||
| "macos", | ||
| device.osVersion, | ||
| device.browser, | ||
| device.browserVersion || "latest", | ||
| ]; | ||
| } else if (device.platform === "android") { | ||
| return [ | ||
| "android", | ||
| device.deviceName, | ||
| device.osVersion, | ||
| device.browser || "chrome", | ||
| ]; | ||
| } else if (device.platform === "ios") { | ||
| return [ | ||
| "ios", | ||
| device.deviceName, | ||
| device.osVersion, | ||
| device.browser || "safari", | ||
| ]; | ||
| } else { | ||
| throw new Error(`Unsupported platform: ${device.platform}`); | ||
| } | ||
| }); |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The device conversion logic contains repetitive code for handling different platforms. The fallback values (browserVersion || "latest", browser || "chrome", browser || "safari") should be documented or centralized. Consider extracting these defaults into constants to ensure consistency across the codebase and make maintenance easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/tools/appautomate.ts
Outdated
| (args.devices || []).length === 0 | ||
| ? [["android", "Samsung Galaxy S24", "latest"]] | ||
| : convertMobileDevicesToTuples(args.devices || []); |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition (args.devices || []).length === 0 checks the array length after defaulting to empty array, then uses args.devices || [] again in the conversion. This pattern is redundant. Simplify to: const devices: Array<Array<string>> = args.devices.length === 0 ? [['android', 'Samsung Galaxy S24', 'latest']] : convertMobileDevicesToTuples(args.devices); since the schema has .default([]) which ensures args.devices is never undefined.
| (args.devices || []).length === 0 | |
| ? [["android", "Samsung Galaxy S24", "latest"]] | |
| : convertMobileDevicesToTuples(args.devices || []); | |
| args.devices.length === 0 | |
| ? [["android", "Samsung Galaxy S24", "latest"]] | |
| : convertMobileDevicesToTuples(args.devices); |
| const inputDevices = input.devices || []; | ||
| const devices: Array<Array<string>> = | ||
| inputDevices.length === 0 | ||
| ? [["android", "Samsung Galaxy S24", "latest"]] | ||
| : inputDevices; | ||
| : convertMobileDevicesToTuples(inputDevices); |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default device [['android', 'Samsung Galaxy S24', 'latest']] is duplicated in both appautomate.ts (line 387) and appium-sdk/handler.ts (line 54). Consider defining this as a constant (e.g., DEFAULT_MOBILE_DEVICE) in a shared location to ensure consistency and ease maintenance.
…g in App Automate tools
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
No description provided.