From ffe16e893236258b2d5cab67508911e5d0aa3929 Mon Sep 17 00:00:00 2001 From: Shirish Kamath Date: Wed, 31 Jan 2024 20:17:30 +0530 Subject: [PATCH] Added detox RN app upload APIs --- openapi.yml | 117 +++++++++++++++++++++++++++++ src/__tests__/app-automate.test.ts | 37 ++++++++- src/app-automate.ts | 38 ++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) diff --git a/openapi.yml b/openapi.yml index d6fdcac..a46c155 100644 --- a/openapi.yml +++ b/openapi.yml @@ -3284,6 +3284,123 @@ paths: "500": $ref: '#/components/schemas/5xx.InternalServerError' + /app-automate/detox/v2/android/app: + post: + summary: Upload an app + description: Upload the application under test (AUT) for Detox Android testing. + operationId: uploadAppAutomateDetoxAndroidApp + requestBody: + required: true + content: + multipart/form-data: + schema: + allOf: + - type: object + properties: + custom_id: + type: string + description: >- + Custom ID for the app. Accepted characters are A-Z, a-z, 0-9, ., -, _. + All other characters are ignored. Character limit is 100. + example: "app_1" + - oneOf: + - type: object + required: [file] + properties: + file: + type: string + format: binary + description: >- + Path to the app file on your machine. Supported file formats are .apk and .aab files + for Android and .ipa file for iOS + - type: object + required: [url] + properties: + url: + type: string + description: >- + URL of the app file. Ensure that its a publicly accessible URL as BrowserStack + will attempt to download the app from this location. Supported file formats are + .apk and .aab files for Android and .ipa file for iOS + example: "https://example.com/app.ipa" + + responses: + "200": + description: Successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/AppAutomateApp" + + "400": + $ref: '#/components/schemas/400.BadRequest' + "401": + $ref: '#/components/schemas/401.Unauthorized' + "404": + $ref: '#/components/schemas/404.NotFound' + "422": + $ref: '#/components/schemas/422.UnprocessableEntity' + "500": + $ref: '#/components/schemas/5xx.InternalServerError' + + /app-automate/detox/v2/android/app-client: + post: + summary: Upload an app client + description: Upload the app client under test for Detox Android testing. + operationId: uploadAppAutomateDetoxAndroidAppClient + requestBody: + required: true + content: + multipart/form-data: + schema: + allOf: + - type: object + properties: + custom_id: + type: string + description: >- + Custom ID for the app. Accepted characters are A-Z, a-z, 0-9, ., -, _. + All other characters are ignored. Character limit is 100. + example: "app_1" + - oneOf: + - type: object + required: [file] + properties: + file: + type: string + format: binary + description: >- + Path to the app file on your machine. Supported file formats are .apk and .aab files + for Android + - type: object + required: [url] + properties: + url: + type: string + description: >- + URL of the app file. Ensure that its a publicly accessible URL as BrowserStack + will attempt to download the app from this location. Supported file formats are + .apk and .aab files for Android + example: "https://example.com/app.apk" + + responses: + "200": + description: Successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/AppAutomateApp" + + "400": + $ref: '#/components/schemas/400.BadRequest' + "401": + $ref: '#/components/schemas/401.Unauthorized' + "404": + $ref: '#/components/schemas/404.NotFound' + "422": + $ref: '#/components/schemas/422.UnprocessableEntity' + "500": + $ref: '#/components/schemas/5xx.InternalServerError' /app-automate/espresso/v2/app: post: diff --git a/src/__tests__/app-automate.test.ts b/src/__tests__/app-automate.test.ts index cc58e6c..05b8149 100644 --- a/src/__tests__/app-automate.test.ts +++ b/src/__tests__/app-automate.test.ts @@ -1,8 +1,8 @@ +import { FlutterPlatform } from "@/app-automate"; import { components } from "@/generated/openapi"; +import { zipSync } from "fflate"; import { describe, expect, expectTypeOf, test } from "vitest"; import type { BrowserStackTestContext } from "./setup"; -import { FlutterPlatform } from "@/app-automate"; -import { zip, zipSync } from "fflate"; describe("AppAutomateClient", () => { @@ -607,4 +607,37 @@ describe("AppAutomateClient", () => { expectTypeOf(data.success.message).toMatchTypeOf(); }); }); + + describe.skip("Detox Android Apps", (test) => { + + test("uploadDetoxApp", async ({ + appAutomate: { client }, + }) => { + const data = await client.uploadDetoxAndroidApp("app", { + url: "https://github.com/aws-samples/aws-device-farm-sample-app-for-android/raw/master/prebuilt/app-debug-androidTest.apk", + filename: "example.apk", + custom_id: "example-app", + }); + + expect(data).toBeDefined(); + expect(data.app_url).toBeDefined(); + expectTypeOf(data.app_url).toMatchTypeOf(); + expectTypeOf(data).toMatchTypeOf(); + }); + + test("uploadDetoxAppClient", async ({ + appAutomate: { client }, + }) => { + const data = await client.uploadDetoxAndroidApp("app-client", { + url: "https://github.com/aws-samples/aws-device-farm-sample-app-for-android/raw/master/prebuilt/app-debug-androidTest.apk", + filename: "example.apk", + custom_id: "example-app", + }); + + expect(data).toBeDefined(); + expect(data.app_url).toBeDefined(); + expectTypeOf(data.app_url).toMatchTypeOf(); + expectTypeOf(data).toMatchTypeOf(); + }); + }); }); diff --git a/src/app-automate.ts b/src/app-automate.ts index 0ac04ef..7540997 100644 --- a/src/app-automate.ts +++ b/src/app-automate.ts @@ -1077,6 +1077,44 @@ export class AppAutomateClient extends APIClient { }, }); } + + uploadDetoxAndroidApp( + type: T, + body: operations[T extends "app" + ? "uploadAppAutomateDetoxAndroidApp" + : "uploadAppAutomateDetoxAndroidAppClient"]["requestBody"]["content"]["multipart/form-data"] & { + filename: string; + }, + options?: APIFetchOptions< + operations[T extends "app" + ? "uploadAppAutomateDetoxAndroidApp" + : "uploadAppAutomateDetoxAndroidAppClient"] + > + ) { + return this.makePostRequest( + type + ? "/app-automate/detox/v2/android/app" + : "/app-automate/detox/v2/android/app-client", + { + ...options, + body, + bodySerializer: () => { + const formData = new FormData(); + if ("file" in body) { + formData.append("file", body.file, body.filename); + } else { + formData.append("url", body.url); + } + + if (body.custom_id) { + formData.append("custom_id", body.custom_id); + } + + return formData; + }, + } + ); + } } export enum FlutterPlatform {