diff --git a/README.md b/README.md
index 2f27a5d..30b61ae 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Add this step directly to your workflow in the [Bitrise Workflow Editor](https:/
Description
-Run your XCUI tests on BrowserStack App Automate. This step collects the built IPA from `$BITRISE_IPA_PATH` and the output bundle file from `$BITRISE_TEST_BUNDLE_PATH` environment variables.
+Run your XCUI tests on BrowserStack App Automate. This step collects *both the built app and test suite* from the `$BITRISE_BUNDLE_PATH` environment variable, generates an IPA file, uploads and starts a test build.
## Configure the Step
@@ -17,17 +17,21 @@ Complete the following steps to configure BrowserStack's XCUI step in Bitrise:
1. Open the Workflow you want to use in the Workflow Editor.
-2. Add the [Xcode Archive & Export for iOS](https://www.bitrise.io/integrations/steps/xcode-archive) and [Xcode Build for testing for iOS](https://www.bitrise.io/integrations/steps/xcode-build-for-test) steps to your workflow and configure them.
+2. Add the [Xcode Build for testing for iOS](https://www.bitrise.io/integrations/steps/xcode-build-for-test) step to your workflow and configure it.
-3. Add the **BrowserStack App Automate - XCUI** step below the **Xcode Archive & Export for iOS** and **Xcode Build for testing for iOS** steps.
+3. Add the **BrowserStack App Automate - XCUI** step below the **Xcode Build for testing for iOS** steps.
4. Add your BrowserStack Username and Access Key in the **Authentication** step input.
+
+5. Provide the built application name in the **iOS app under test** input. This is typically the product name in your project.
-5. For the **iOS app under test** input, the **BITRISE_IPA_PATH** output variable from the **Xcode Archive & Export for iOS** step exports the IPA file. Add `$BITRISE_IPA_PATH` to the **iOS app under test** input.
For the **XCUI test suite** input, the **BITRISE_TEST_BUNDLE_PATH** output variable from the **Xcode Build for testing for iOS step** exports the test suite. Add `$BITRISE_TEST_BUNDLE_PATH` to the **iOS app under test** input.
If you are not using **Xcode Archive & Export for iOS** and **Xcode Build for testing for iOS** steps, ensure that the **iOS app under test** input points to the path of your app (`.ipa` file). Also, ensure that the **XCUI test suite** input points to the test suite runner file. In the case of the runner app, it should be in the `/Debug-iphoneos` directory if you are providing an absolute path.
+6. For the **XCUI test suite** input, the **BITRISE_TEST_BUNDLE_PATH** output variable from the **Xcode Build for testing for iOS step** indicates where the app bundle and test suite are located. Add `$BITRISE_TEST_BUNDLE_PATH` to the **iOS app under test** input.
If you are not using the **Xcode Build for testing for iOS** step, ensure that the **XCUI test suite** input points to a directory that contains both the test suite runner file and the app bundle (not .ipa).
-6. Add one or more devices in the **Devices** step input.
+7. Add one or more devices in the **Devices** step input.
-7. Configure additional step inputs like **Debug logs** and **Test Configurations** and start your build.
+8. Optionally provide custom IDs for the app and test suite in **Custom IDs** and configure additional step inputs like **Debug logs** and **Test Configurations**.
+
+9. Start your build.
@@ -38,9 +42,11 @@ Complete the following steps to configure BrowserStack's XCUI step in Bitrise:
| Key | Description | Flags | Default |
| --- | --- | --- | --- |
-| `iOS app` | Set the path of the app (.ipa) file. | Required | N/A |
+| `iOS app under test` | Set the name of the .app file (same as `PRODUCT_NAME` under Packaging in Xcode Build Settings). | Required | N/A |
| `XCUI test suite` | Set the path of the output bundle file. | Required | N/A |
| `Devices` | Provide one or more device-OS combination in a new line. For example:
`iPhone 11-13`
`iPhone XS-15` | Required | N/A |
+| `App Custom ID` | Custom identifier for the app under testing. | Optional | N/A |
+| `Test Suite Custom ID` | Custom identifier for the test suite to be run. | Optional | N/A |
| `Instrumentation logs` | Generate instrumentation logs of the test session | Optional | `true` |
| `Network logs` | Generate network logs of your test sessions to capture network traffic, latency, etc. | Optional | `false` |
| `Device Logs` | Generate device logs | Optional | `false` |
diff --git a/bitrise.yml b/bitrise.yml
index 21271f9..3b66b00 100644
--- a/bitrise.yml
+++ b/bitrise.yml
@@ -7,7 +7,7 @@ app:
- A_SECRET_PARAM: $A_SECRET_PARAM
# If you want to share this step into a StepLib
- BITRISE_STEP_ID: xcui-browserstack-official
- - BITRISE_STEP_VERSION: "1.0.0"
+ - BITRISE_STEP_VERSION: "1.1.0"
- BITRISE_STEP_GIT_CLONE_URL: https://github.com/browserstack/browserstack-bitrise-xcui-step.git
- MY_STEPLIB_REPO_FORK_GIT_URL: $MY_STEPLIB_REPO_FORK_GIT_URL
diff --git a/constants.go b/constants.go
index c686f7d..303abc3 100644
--- a/constants.go
+++ b/constants.go
@@ -18,10 +18,14 @@ const (
UPLOAD_APP_ERROR = "Failed to upload app on BrowserStack, error : %s"
FILE_NOT_AVAILABLE_ERROR = "Failed to upload test suite on BrowserStack, error: file not available"
INVALID_FILE_TYPE_ERROR = "Failed to upload test suite on BrowserStack, error: invalid file type"
+ APP_CUSTOM_ID_ERROR = "Failed to attach app custom ID, error: %s"
BUILD_FAILED_ERROR = "Failed to execute build on BrowserStack, error: %s"
FETCH_BUILD_STATUS_ERROR = "Failed to fetch test results, error: %s"
HTTP_ERROR = "Something went wrong while processing your request, error: %s"
- RUNNER_APP_NOT_FOUND = "xcuitest_testsuite_path: couldn’t find the -Runner.app . Please add the $BITRISE_TEST_BUNDLE_PATH from Xcode Build for testing for iOS step or the absolute path of -Runner.app"
- IPA_NOT_FOUND = "app_ipa_path: couldn’t find the iOS app (.ipa file). Please add the $BITRISE_IPA_PATH from Xcode Archive & Export for iOS step or the absolute path of iOS app (.ipa file)"
- FILE_ZIP_ERROR = "Something went wrong while processing the test-suite, error: %s"
+ RUNNER_APP_NOT_FOUND = "xcuitest_testsuite_path: couldn’t find the -Runner.app. Please add the $BITRISE_TEST_BUNDLE_PATH from Xcode Build for testing for iOS step or the absolute path of -Runner.app"
+ IPA_NOT_FOUND = "Failed to generate an .ipa file. Please verify the value in $BUNDLE_APP_NAME"
+ FILE_NOT_FOUND = "File not found: %s"
+ FILE_COPY_ERROR = "Failed to copy file, error: %s"
+ FILE_DIR_ERROR = "Failed to create directory, error: %s"
+ FILE_ZIP_ERROR = "Failed to zip file, error: %s"
)
diff --git a/main.go b/main.go
index c3f839f..8a99992 100644
--- a/main.go
+++ b/main.go
@@ -17,32 +17,36 @@ func main() {
username := os.Getenv("browserstack_username")
access_key := os.Getenv("browserstack_accesskey")
- ios_app := os.Getenv("app_ipa_path")
+ app_bundle_name := os.Getenv("app_bundle_name")
+ app_custom_id := os.Getenv("app_custom_id")
+ test_suite_custom_id := os.Getenv("test_suite_custom_id")
test_suite_path := os.Getenv("xcui_test_suite")
if username == "" || access_key == "" {
failf(UPLOAD_APP_ERROR, "invalid credentials")
}
- if ios_app == "" {
- failf(IPA_NOT_FOUND)
- }
-
if test_suite_path == "" {
failf(RUNNER_APP_NOT_FOUND)
}
+ ipa_file_name := locateAppBundleFileAndIpa(test_suite_path, app_bundle_name)
find_and_zip_file_err := locateTestRunnerFileAndZip(test_suite_path)
+ if ipa_file_name == "" {
+ failf(IPA_NOT_FOUND)
+ }
+
if find_and_zip_file_err != nil {
failf(find_and_zip_file_err.Error())
}
+ test_app_app := ipa_file_name
test_runner_app := TEST_RUNNER_ZIP_FILE_NAME
log.Print("Uploading app on BrowserStack App Automate")
- upload_app, err := upload(ios_app, APP_UPLOAD_ENDPOINT, username, access_key)
+ upload_app, err := upload(test_app_app, APP_UPLOAD_ENDPOINT, &app_custom_id, username, access_key)
if err != nil {
failf(err.Error())
@@ -60,7 +64,7 @@ func main() {
log.Print("Uploading test suite on BrowserStack App Automate")
- upload_test_suite, err := upload(test_runner_app, TEST_SUITE_UPLOAD_ENDPOINT, username, access_key)
+ upload_test_suite, err := upload(test_runner_app, TEST_SUITE_UPLOAD_ENDPOINT, &test_suite_custom_id, username, access_key)
if err != nil {
failf(err.Error())
diff --git a/main_test.go b/main_test.go
index ae730ce..9d58749 100644
--- a/main_test.go
+++ b/main_test.go
@@ -36,7 +36,7 @@ func TestBuild(t *testing.T) {
func TestUpload(t *testing.T) {
t.Log("It should throw file not found error with empty path")
{
- build, err := upload("", APP_UPLOAD_ENDPOINT, "username", "password")
+ build, err := upload("", APP_UPLOAD_ENDPOINT, nil, "username", "password")
t.Log(build, err)
require.Equal(t, "", build)
require.Error(t, err)
@@ -44,7 +44,7 @@ func TestUpload(t *testing.T) {
t.Log("It should throw file not found error with invalid path")
{
- build, err := upload("invalidpath", APP_UPLOAD_ENDPOINT, "username", "password")
+ build, err := upload("invalidpath", APP_UPLOAD_ENDPOINT, nil, "username", "password")
t.Log(build, err)
require.Equal(t, "", build)
require.Error(t, err)
diff --git a/services.go b/services.go
index 589904e..5cd2a76 100644
--- a/services.go
+++ b/services.go
@@ -54,7 +54,7 @@ func build(app_url string, test_suite_url string, username string, access_key st
}
// this function uploads both app and test suite
-func upload(app_path string, endpoint string, username string, access_key string) (string, error) {
+func upload(app_path string, endpoint string, custom_id *string, username string, access_key string) (string, error) {
if app_path == "" {
return "", errors.New(FILE_NOT_AVAILABLE_ERROR)
}
@@ -84,6 +84,14 @@ func upload(app_path string, endpoint string, username string, access_key string
return "", errors.New(FILE_NOT_AVAILABLE_ERROR)
}
+ if custom_id != nil {
+ fileErr := multipart_writer.WriteField("custom_id", *custom_id)
+
+ if fileErr != nil {
+ return "", errors.New(APP_CUSTOM_ID_ERROR)
+ }
+ }
+
err := multipart_writer.Close()
if err != nil {
diff --git a/step.yml b/step.yml
index 7da4f1a..5ddbc03 100644
--- a/step.yml
+++ b/step.yml
@@ -12,7 +12,7 @@ title: |-
summary: |
Run your XCUITest tests on BrowserStack App Automate
description: |
- Run your XCUITest tests on BrowserStack App Automate. This step collects the built IPA from `$BITRISE_IPA_PATH` and test suite from `$BITRISE_BUNDLE_PATH` Environment Variables
+ Run your XCUITest tests on BrowserStack App Automate. This step collects the built app and test suite from `$BITRISE_BUNDLE_PATH` environment variable
website: https://github.com/browserstack/browserstack-bitrise-xcui-step
source_code_url: https://github.com/browserstack/browserstack-bitrise-xcui-step
support_url: https://github.com/browserstack/browserstack-bitrise-xcui-step/issues
@@ -78,14 +78,14 @@ inputs:
is_sensitive: true
description: 'Access Key of the BrowserStack account'
- # IPA's
- - app_ipa_path: $BITRISE_IPA_PATH
+ # App and test suite
+ - app_bundle_name: $BUNDLE_APP_NAME
opts:
title: 'iOS app under test'
- summary: 'Path to the app (.ipa) file'
+ summary: 'Name of the .app file'
is_expand: true
is_required: true
- description: 'Path of the app (.ipa) file'
+ description: 'Name of the .app file generated when building for testing'
- xcui_test_suite: $BITRISE_TEST_BUNDLE_PATH
opts:
title: 'XCUI test suite'
@@ -111,6 +111,24 @@ inputs:
is_expand: true
is_required: true
+ # Custom IDs
+ - app_custom_id:
+ opts:
+ title: 'App Custom ID'
+ summary: 'App Custom ID in BrowserStack'
+ is_expand: true
+ is_required: false
+ description: 'App Custom ID in BrowserStack'
+ category: 'Customs IDs'
+ - test_suite_custom_id:
+ opts:
+ title: 'Test Suite Custom ID'
+ summary: 'Test Suite Custom ID in BrowserStack'
+ is_expand: true
+ is_required: false
+ description: 'Test Suite Custom ID in BrowserStack'
+ category: 'Customs IDs'
+
# Debug logs inputs
- instrumentation_logs: "true"
opts:
diff --git a/structs.go b/structs.go
index 3d0741b..12d1d56 100644
--- a/structs.go
+++ b/structs.go
@@ -9,7 +9,7 @@ type TestMapping struct {
type TestSharding struct {
NumberOfShards int `json:"numberOfShards,omitempty"`
Mapping []TestMapping `json:"mapping,omitempty"`
- AutoStrategyDevices []string `json:"devices,omitempty"`
+ AutoStrategyDevices string `json:"deviceSelection,omitempty"`
}
type BrowserStackPayload struct {
diff --git a/util_fns.go b/util_fns.go
index 0f1d7d9..316dd8d 100644
--- a/util_fns.go
+++ b/util_fns.go
@@ -270,31 +270,61 @@ func WalkMatch(root, ext string) []string {
return files_found
}
-func locateTestRunnerFileAndZip(test_suite_location string) error {
- split_test_suite_path := strings.Split(test_suite_location, "/")
- get_file_name := split_test_suite_path[len(split_test_suite_path)-1]
+func locateAppFile(location string, file_name string) string {
+ app_extension := "app"
+ file_name_and_extension := file_name + "." + app_extension
- test_runner_app_path := ""
+ split_path := strings.Split(location, "/")
+ get_file_name := split_path[len(split_path)-1]
- check_file_extension := strings.Split(get_file_name, ".")
+ file_path := ""
- // Checking 2 conditions here
- // 1. test_suite_location - is this runner app
- // 2. test_suite_location - if this is a directory, does any runner app exists in this directory.
- if len(check_file_extension) > 0 && check_file_extension[len(check_file_extension)-1] == "app" {
- test_runner_app_path = test_suite_location
+ // If location is already .app file, return that. Else if location is directory,
+ // check if it contains any .app files with the specified name.
+ check_file_extension := strings.Split(get_file_name, ".")
+ if len(check_file_extension) > 0 && check_file_extension[len(check_file_extension)-1] == app_extension {
+ file_path = location
} else if strings.Contains(get_file_name, "test_bundle") {
- // if test_suite_location is a directory instead of the file, then check if runner app exits
- files := WalkMatch(test_suite_location+"/Debug-iphoneos/", "*-Runner.app")
+ files := WalkMatch(location+"/Debug-iphoneos/", file_name_and_extension)
if len(files) < 1 {
- return errors.New(RUNNER_APP_NOT_FOUND)
+ failf(FILE_NOT_FOUND, file_name_and_extension)
}
- test_runner_app_path = files[len(files)-1]
+ file_path = files[len(files)-1]
} else {
- return errors.New(RUNNER_APP_NOT_FOUND)
+ failf(FILE_NOT_FOUND, file_name_and_extension)
+ }
+
+ return file_path
+}
+
+// Locates .app, moves it into a Payload folder and compresses that folder into .ipa.
+func locateAppBundleFileAndIpa(app_bundle_location string, app_bundle_name string) string {
+ app_bundle_path := locateAppFile(app_bundle_location, app_bundle_name)
+ app_zip_name := app_bundle_name + ".ipa"
+
+ _, mkdir_err := exec.Command("mkdir", "Payload").Output()
+ if mkdir_err != nil {
+ failf(FILE_DIR_ERROR, mkdir_err)
+ }
+
+ _, err := exec.Command("cp", "-r", app_bundle_path, "Payload/Application.app").Output()
+ if err != nil {
+ failf(FILE_COPY_ERROR, err)
+ }
+
+ _, zipping_err := exec.Command("zip", "-r", "-D", app_zip_name, "Payload").Output()
+ if zipping_err != nil {
+ failf(FILE_ZIP_ERROR, zipping_err)
}
+ return app_zip_name
+}
+
+// Locates runner .app and compresses it into .zip.
+func locateTestRunnerFileAndZip(test_suite_location string) error {
+ test_runner_app_path := locateAppFile(test_suite_location, "*-Runner")
+
file_path := strings.Split(test_runner_app_path, "/")
test_runner_file_name := file_path[len(file_path)-1]