diff --git a/packages/aws-device-farm/iosResources/appiumTest/index.ts b/packages/aws-device-farm/iosResources/appiumTest/index.ts new file mode 100644 index 00000000..b549e37a --- /dev/null +++ b/packages/aws-device-farm/iosResources/appiumTest/index.ts @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { RemoteOptions, remote } from "webdriverio"; + +const capabilities = { + /* + platformName: "iOS", + "appium:automationName": "XCUITest", + "appium:xcodeOrgId": "Z445H6455F", + "appium:xcodeSigningId": "iPhone Developer", + "appium:bundleId": "org.reactjs.native.example.fakeStoreBam", + "appium:udid": "00008020-001C48210CF3002E", //process.env.DEVICEFARM_DEVICE_UDID_FOR_APPIUM, + "appium:deviceName": "iPhone de Developers (3) (16.1)", //process.env.DEVICEFARM_DEVICE_NAME, + //"appium:platformVersion": process.env.DEVICEFARM_DEVICE_OS_VERSION, + //"appium:app": process.env.DEVICEFARM_APP_PATH, + */ +}; + +const wdOpts: RemoteOptions = { + hostname: "0.0.0.0", + port: 4723, + logLevel: "info", + capabilities, +}; + +async function runTest() { + const driver = await remote(wdOpts); + try { + const buttonItem = await driver.$('//XCUIElementTypeButton[@name="VIENS ON PETE TOUT"]'); + await buttonItem.click(); + const textButton = "ON A TOUT PETE"; + await driver.waitUntil( + async () => { + const text = await buttonItem.getText(); + return text === textButton; + }, + { timeout: 5000, timeoutMsg: "Expected text to be different after 5s" } + ); + } finally { + await driver.pause(1000); + await driver.deleteSession(); + } +} + +runTest().catch(console.error); diff --git a/packages/aws-device-farm/iosResources/appiumTest/package.json b/packages/aws-device-farm/iosResources/appiumTest/package.json new file mode 100644 index 00000000..a71d9d4a --- /dev/null +++ b/packages/aws-device-farm/iosResources/appiumTest/package.json @@ -0,0 +1,15 @@ +{ + "name": "appiumTest", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "webdriverio": "^8.14.3" + }, + "resolutions": { + "strip-ansi": "^6.0.0" + }, + "bundledDependencies": [ + "webdriverio" + ] +} diff --git a/packages/aws-device-farm/iosResources/appiumTest/tsconfig.json b/packages/aws-device-farm/iosResources/appiumTest/tsconfig.json new file mode 100644 index 00000000..391488ab --- /dev/null +++ b/packages/aws-device-farm/iosResources/appiumTest/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "module": "commonjs" + } +} diff --git a/packages/aws-device-farm/iosResources/fakeStore.ipa b/packages/aws-device-farm/iosResources/fakeStore.ipa new file mode 100644 index 00000000..56f3b09c Binary files /dev/null and b/packages/aws-device-farm/iosResources/fakeStore.ipa differ diff --git a/packages/aws-device-farm/iosResources/ios.yml b/packages/aws-device-farm/iosResources/ios.yml new file mode 100644 index 00000000..4a5ac66c --- /dev/null +++ b/packages/aws-device-farm/iosResources/ios.yml @@ -0,0 +1,111 @@ +version: 0.1 + +# Phases are collection of commands that get executed on Device Farm. +phases: + # The install phase includes commands that install dependencies that your tests use. + # Default dependencies for testing frameworks supported on Device Farm are already installed. + install: + commands: + # This test execution environment uses Appium version 1.9.1 by default, however we enable you to change it using the Appium version manager (avm) + # An example "avm" command below changes the version to 1.19.0 + # For your convenience, we have pre-installed the following Appium versions: 1.9.1, 1.10.1, 1.11.1, 1.12.1, 1.13.0, 1.14.1, 1.14.2, 1.15.1, 1.16.0, 1.17.1, 1.18.0, 1.18.1, 1.18.2, 1.18.3, and 1.19.0 + # For iOS Devices on OS version 14.2 and above, please use Appium Version 1.19.0 or higher. + # For iOS devices on OS version 14.0 and above, please use Appium version 1.18.0 or higher. + # For iOS devices on OS version 13.4 through 13.7, please use Appium version 1.17.1 or higher. + # Additionally, for iOS devices on OS version 13.0 through 13.3, please use Appium version 1.16.0 or higher. + # To use one of these Appium versions, change the version number in the "avm" command below to your desired version: + - export NVM_DIR=$HOME/.nvm + - . $NVM_DIR/nvm.sh + - nvm install 20 + - uname -m + - export APPIUM_VERSION=2.0.1 + - avm $APPIUM_VERSION + - echo $APPIUM_VERSION + - mkdir /usr/local/avm/versions/$APPIUM_VERSION/node_modules/appium/bin + - ln -s /usr/local/avm/versions/$APPIUM_VERSION/node_modules/.bin/appium /usr/local/avm/versions/$APPIUM_VERSION/node_modules/appium/bin/appium.js + + # Device farm provides different pre-built versions of WebDriverAgent, and each is suggested for different versions of Appium: + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V6: this version is suggested for Appium 1.18.2, 1.18.3, and 1.19.0. V6 is built from the following source code: https://github.com/appium/WebDriverAgent/releases/tag/v2.20.8 + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V5: this version is suggested for Appium 1.18.0 and 1.18.1. V5 is built from the following source code: https://github.com/appium/WebDriverAgent/releases/tag/v2.20.2 + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V4: this version is suggested for Appium 1.17.1. V4 is built from the following source code: https://github.com/appium/WebDriverAgent/releases/tag/v2.14.1 + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V3: this version is suggested for Appium 1.16.0. V3 is built from the following source code: https://github.com/appium/WebDriverAgent/releases/tag/v2.3.2 + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V2: this version is suggested for Appium 1.15.1. V2 is built from the following source code: https://github.com/appium/WebDriverAgent/tree/v1.3.5 + # DEVICEFARM_WDA_DERIVED_DATA_PATH_V1: this version is suggested for Appium 1.9.1 through 1.14.2. V1 is built from the following source code: https://github.com/appium/WebDriverAgent/tree/2dbbf917ec2e4707bae9260f701d43c82b55e1b9 + # We will automatically configure your WebDriverAgent version based on your Appium version using the following code. + + # For users of Appium versions 1.15.0 and higher, your Appium version requires that the UDID of the device not contain any "-" characters + # So, we will create a new environment variable of the UDID specifically for Appium based on your Appium version + - >- + DEVICEFARM_DEVICE_UDID_FOR_APPIUM=$(echo $DEVICEFARM_DEVICE_UDID | tr -d "-"); + DEVICEFARM_WDA_DERIVED_DATA_PATH=$DEVICEFARM_WDA_DERIVED_DATA_PATH_V6; + + # By default the node version installed is 10.9.0 + # you can switch to an alternate node version using below command. + # - nvm install 10.13.0 + + # Unpackage and install the node modules that you uploaded in the test phase. + - echo "Navigate to test package directory" + - cd $DEVICEFARM_TEST_PACKAGE_PATH + - npm install *.tgz + # Install appium driver for xcuitest + - appium driver install xcuitest + + # The pre-test phase includes commands that set up your test environment. + pre_test: + commands: + # We recommend starting the appium server process in the background using the command below. + # Appium server log will go to $DEVICEFARM_LOG_DIR directory. + # The environment variables below will be auto-populated during run time. + - echo "Start appium server" + - echo $DEVICEFARM_DEVICE_PLATFORM_NAME + - >- + appium server --log-timestamp --default-capabilities "{\"appium:usePrebuiltWDA\": true, \"appium:derivedDataPath\":\"$DEVICEFARM_WDA_DERIVED_DATA_PATH\", + \"appium:deviceName\": \"$DEVICEFARM_DEVICE_NAME\", \"appium:platformName\":\"$DEVICEFARM_DEVICE_PLATFORM_NAME\", \"appium:app\":\"$DEVICEFARM_APP_PATH\", + \"appium:automationName\":\"XCUITest\", \"appium:udid\":\"$DEVICEFARM_DEVICE_UDID_FOR_APPIUM\", \"appium:platformVersion\":\"$DEVICEFARM_DEVICE_OS_VERSION\"}" + >> $DEVICEFARM_LOG_DIR/appiumlog.txt 2>&1 & + + - >- + start_appium_timeout=0; + while [ true ]; + do + if [ $start_appium_timeout -gt 60 ]; + then + echo "appium server never started in 60 seconds. Exiting"; + exit 1; + fi; + grep -i "Appium REST http interface listener started on http://0.0.0.0:4723" $DEVICEFARM_LOG_DIR/appiumlog.txt >> /dev/null 2>&1; + if [ $? -eq 0 ]; + then + echo "Appium REST http interface listener started on http://0.0.0.0:4723"; + break; + else + echo "Waiting for appium server to start. Sleeping for 1 second"; + sleep 1; + start_appium_timeout=$((start_appium_timeout+1)); + fi; + done; + + # The test phase includes commands that start your test suite execution. + test: + commands: + # Your test package is downloaded in $DEVICEFARM_TEST_PACKAGE_PATH + # However, we must navigate to its subdirectory "node_modules/*", as this directory has your test code and dependency node modules + - echo "Navigate to test code directory" + - cd $DEVICEFARM_TEST_PACKAGE_PATH/node_modules/* + - npm install + + - echo "Start Appium Node test" + # Enter the command below to start the tests . The command should be similar to what you use to run the tests locally. + # For e.g. assuming you run your tests locally using command "node YOUR_TEST_FILENAME.js.", enter the same command below: + - npx ts-node index.ts + + # The post test phase includes commands that are run after your tests are executed. + post_test: + commands: + +# The artifacts phase lets you specify the location where your tests logs, device logs will be stored. +# And also let you specify the location of your test logs and artifacts which you want to be collected by Device Farm. +# These logs and artifacts will be available through ListArtifacts API in Device Farm. +artifacts: + # By default, Device Farm will collect your artifacts from following directories + - $DEVICEFARM_LOG_DIR diff --git a/packages/aws-device-farm/src/commands/runTest.ts b/packages/aws-device-farm/src/commands/runTest.ts index 7fdab27c..2bd8be15 100644 --- a/packages/aws-device-farm/src/commands/runTest.ts +++ b/packages/aws-device-farm/src/commands/runTest.ts @@ -13,6 +13,7 @@ import { createDefaultNodeTestPackage, DEFAULT_TEST_PACKAGE_NAME, } from "./createDefaultNodeTestPackage"; +import { getUploadType } from "../utils/getUploadType"; export const DEFAULT_RUN_TEST_OPTIONS = { projectName: "Flashlight", @@ -77,6 +78,7 @@ export const runTest = async ({ filePath: testFolderZipPath, type: UploadType.APPIUM_NODE_TEST_PACKAGE, }); + fs.rmSync(testFolderZipPath); } let apkUploadArn; @@ -84,10 +86,14 @@ export const runTest = async ({ if (apkUploadArnGiven) { apkUploadArn = apkUploadArnGiven; } else if (apkPath) { + const uploadType = getUploadType(apkPath); + if (!uploadType) { + throw new Error(`Cannot find upload type for file: ${apkPath}`); + } apkUploadArn = await uploadRepository.upload({ projectArn, filePath: apkPath, - type: UploadType.ANDROID_APP, + type: uploadType, }); } else { throw new Error("Neither apkUploadArn nor apkPath was passed."); @@ -105,7 +111,10 @@ export const runTest = async ({ filePath: testSpecPath, type: UploadType.APPIUM_NODE_TEST_SPEC, }); - fs.rmSync(testSpecPath); + // Clean up test spec file only if it was created on the fly + if (!testSpecsPathGiven) { + fs.rmSync(testSpecPath); + } Logger.info("Starting test run..."); const testRunArn = await testRepository.scheduleRun({ diff --git a/packages/aws-device-farm/src/utils/getUploadType.ts b/packages/aws-device-farm/src/utils/getUploadType.ts new file mode 100644 index 00000000..060708f0 --- /dev/null +++ b/packages/aws-device-farm/src/utils/getUploadType.ts @@ -0,0 +1,11 @@ +import { UploadType } from "@aws-sdk/client-device-farm"; +// Will be useful when we replace all apkPath with appPath to have a clear distinction between Android and iOS +export const getUploadType = ( + appPath: string +): UploadType.ANDROID_APP | UploadType.IOS_APP | undefined => { + return appPath.endsWith(".apk") + ? UploadType.ANDROID_APP + : appPath.endsWith(".ipa") + ? UploadType.IOS_APP + : undefined; +}; diff --git a/packages/aws-device-farm/src/utils/unzip.ts b/packages/aws-device-farm/src/utils/unzip.ts index c18d7aef..2f4ed78e 100644 --- a/packages/aws-device-farm/src/utils/unzip.ts +++ b/packages/aws-device-farm/src/utils/unzip.ts @@ -4,11 +4,10 @@ import fs from "fs"; export const unzip = (path: string, destinationFolder: string) => { const zip = new AdmZip(path); - const zipEntries = zip.getEntries(); - - for (const zipEntry of zipEntries) { - const parts = zipEntry.entryName.split("/"); - - fs.writeFileSync(`${destinationFolder}/${parts[parts.length - 1]}`, zipEntry.getData()); - } + zip.getEntries().forEach((zipEntry) => { + if (!zipEntry.isDirectory) { + const parts = zipEntry.entryName.split("/"); + fs.writeFileSync(`${destinationFolder}/${parts[parts.length - 1]}`, zipEntry.getData()); + } + }); }; diff --git a/packages/aws-device-farm/src/zipTestFolder.ts b/packages/aws-device-farm/src/zipTestFolder.ts index a8c8878d..75b767d9 100644 --- a/packages/aws-device-farm/src/zipTestFolder.ts +++ b/packages/aws-device-farm/src/zipTestFolder.ts @@ -8,7 +8,9 @@ export const zipTestFolder = (folderPath: string) => { Logger.info(`Compressing into zip`); const zipPath = `${path.basename(path.resolve(folderPath))}.zip`; - execSync(`cd ${folderPath} && zip -r ${zipPath} *.tgz && rm ${folderPath}/*.tgz`); + execSync(`cd ${folderPath} && zip -r ${zipPath} *.tgz`); + // Had to separate into two commands because else it would fail + execSync(`rm ${folderPath}/*.tgz`); Logger.success(`Zip created, ready for upload: ${zipPath}`); return `${folderPath}/${zipPath}`;