├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .editorconfig ├── .github ├── actions │ ├── install │ │ └── action.yml │ ├── setup │ │ └── action.yml │ └── upload │ │ └── action.yml └── workflows │ ├── bump.yml │ ├── e2e-tests.yml │ ├── github-actions-tests.yml │ ├── integration-tests.yml │ ├── lint.yml │ ├── merge-bump.yml │ ├── pr-bump.yml │ ├── promote.yml │ ├── publish.yml │ ├── pull-requests.yml │ ├── push-branch.yml │ ├── rollback.yml │ ├── upload.yml │ └── validate.yml ├── .gitignore ├── .husky └── pre-commit ├── .mocharc.json ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── autify-cli.rb ├── bin ├── dev ├── dev.cmd ├── run └── run.cmd ├── e2e-test ├── jest.config.js └── mobile │ ├── delay.ts │ ├── get-autify-cli-path.ts │ ├── interact-with-process.ts │ ├── local-execution-flow.test.ts │ ├── pickup-one-simulator.ts │ └── terminate-start-process.ts ├── eslint.config.mjs ├── install-cicd.bash ├── install-standalone.sh ├── integration-test ├── .gitignore ├── LICENSE ├── __recordings__ │ ├── mobile%20build%20upload%20android.apk%20--workspace-id%3D4yyFEL%20--json │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20build%20upload%20android.apk%20--workspace-id%3D4yyFEL │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20build%20upload%20ios.app%20--workspace-id%3D4yyFEL │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20build%20upload%20ios.ipa%20--workspace-id%3D4yyFEL │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2F1EtZ0P%20--build-path%3Dios.ipa%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FWptd97%20--build-id%3Dd1ulrD%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FWptd97%20--build-id%3Dd1ulrD │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FWptd97%20--build-path%3Dandroid.apk%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FWptd97%20--build-path%3Dandroid.apk │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FkYtlRR%20--build-path%3Dios.app%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── mobile%20test%20run%20https%3A%2F%2Fmobile-app.autify.com%2Fprojects%2F4yyFEL%2Ftest_plans%2FkYtlRR%20--build-path%3Dios.app │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Fscenarios%2F91437%20--wait%20--autify-connect-client │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Fscenarios%2F91437%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Fscenarios%2F91437%20-r%3Dhttps%3A%2F%2Fexample.com%20https%3A%2F%2Fexample.net%3Ffoo%3Dbar │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Fscenarios%2F91437%20-r%3Dhttps%3A%2F%2Fexample.com%3Dhttps%3A%2F%2Fexample.net%3Ffoo%3Dbar │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Fscenarios%2F91437 │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Ftest_plans%2F169408%20--wait%20--autify-connect-client │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ ├── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Ftest_plans%2F169408%20--wait │ │ └── polly-proxy_3343057686 │ │ │ └── recording.har │ └── web%20test%20run%20https%3A%2F%2Fapp.autify.com%2Fprojects%2F743%2Ftest_plans%2F169408 │ │ └── polly-proxy_3343057686 │ │ └── recording.har ├── __snapshots__ │ └── golden │ │ ├── mobileBuildUpload.test.js.snap │ │ ├── mobileBuildUploadIos.test.js.snap │ │ ├── mobileBuildUploadIosIPA.test.js.snap │ │ ├── mobileBuildUploadJson.test.js.snap │ │ ├── mobileTestRun.test.js.snap │ │ ├── mobileTestRunAndroid.test.js.snap │ │ ├── mobileTestRunAndroidWait.test.js.snap │ │ ├── mobileTestRunIos.test.js.snap │ │ ├── mobileTestRunIosIPAWait.test.js.snap │ │ ├── mobileTestRunIosWait.test.js.snap │ │ ├── mobileTestRunWait.test.js.snap │ │ ├── webTestRun.test.js.snap │ │ ├── webTestRunAutifyConnectClient.test.js.snap │ │ ├── webTestRunTestPlan.test.js.snap │ │ ├── webTestRunTestPlanAutifyConnectClient.test.js.snap │ │ ├── webTestRunTestPlanWait.test.js.snap │ │ ├── webTestRunUrlReplacements.test.js.snap │ │ ├── webTestRunUrlReplacementsDeprecated.test.js.snap │ │ └── webTestRunWait.test.js.snap ├── bin │ ├── autify-cli-integration-test.js │ ├── autify-mobile-generate-fake-app.js │ └── autify-with-proxy.js ├── jest.config.cjs ├── package.json ├── snapshot-resolver.cjs ├── src │ ├── bin │ │ ├── autify-cli-integration-test.ts │ │ ├── autify-mobile-generate-fake-app.ts │ │ └── autify-with-proxy.ts │ ├── commands.ts │ └── test │ │ ├── golden │ │ ├── mobileBuildUpload.test.ts │ │ ├── mobileBuildUploadIos.test.ts │ │ ├── mobileBuildUploadIosIPA.test.ts │ │ ├── mobileBuildUploadJson.test.ts │ │ ├── mobileTestRun.test.ts │ │ ├── mobileTestRunAndroid.test.ts │ │ ├── mobileTestRunAndroidWait.test.ts │ │ ├── mobileTestRunIos.test.ts │ │ ├── mobileTestRunIosIPAWait.test.ts │ │ ├── mobileTestRunIosWait.test.ts │ │ ├── mobileTestRunWait.test.ts │ │ ├── webTestRun.test.ts │ │ ├── webTestRunAutifyConnectClient.test.ts │ │ ├── webTestRunTestPlan.test.ts │ │ ├── webTestRunTestPlanAutifyConnectClient.test.ts │ │ ├── webTestRunTestPlanWait.test.ts │ │ ├── webTestRunUrlReplacements.test.ts │ │ ├── webTestRunUrlReplacementsDeprecated.test.ts │ │ └── webTestRunWait.test.ts │ │ └── helpers │ │ ├── execAutifyCli.ts │ │ └── testAutifyCliSnapshot.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── renovate.json ├── scripts ├── generate-api-commands.ts └── release.ts ├── src ├── autify │ ├── connect │ │ ├── accessPointConfig.ts │ │ ├── client-manager │ │ │ ├── AccessPoint.ts │ │ │ ├── ClientManager.ts │ │ │ ├── DebugServerClient.ts │ │ │ ├── Logger.ts │ │ │ └── StateMachine.ts │ │ └── installClient.ts │ ├── getWaitIntervalSecond.ts │ ├── mobile │ │ ├── createZip.ts │ │ ├── getBuildDetailUrl.ts │ │ ├── getMobileClient.ts │ │ ├── getTestResultUrl.ts │ │ ├── inspectBuildFile.ts │ │ ├── mobilelink │ │ │ ├── installBinary.ts │ │ │ └── mobile-link-manager │ │ │ │ ├── Logger.ts │ │ │ │ ├── MobileLinkManager.ts │ │ │ │ └── StateMachine.ts │ │ ├── parseTestPlanUrl.ts │ │ ├── parseTestResultUrl.ts │ │ ├── uploadBuild.ts │ │ └── waitTestResult.ts │ └── web │ │ ├── getTestResultUrl.ts │ │ ├── getWebClient.ts │ │ ├── parseAutifyTestUrl.ts │ │ ├── parseTestResultUrl.ts │ │ ├── runTest.ts │ │ └── waitTestResult.ts ├── commands │ ├── connect │ │ ├── access-point │ │ │ ├── create.ts │ │ │ └── set.ts │ │ └── client │ │ │ ├── install.ts │ │ │ └── start.ts │ ├── mobile │ │ ├── api │ │ │ ├── describe-test-result.ts │ │ │ ├── list-test-results.ts │ │ │ ├── run-test-plan.ts │ │ │ └── upload-build.ts │ │ ├── auth │ │ │ └── login.ts │ │ ├── build │ │ │ └── upload.ts │ │ ├── link │ │ │ ├── doctor.ts │ │ │ ├── exec.ts │ │ │ ├── install.ts │ │ │ ├── setup.ts │ │ │ └── start.ts │ │ └── test │ │ │ ├── run.ts │ │ │ └── wait.ts │ └── web │ │ ├── api │ │ ├── create-access-point.ts │ │ ├── create-test-plan-variable.ts │ │ ├── create-url-replacement.ts │ │ ├── delete-access-point.ts │ │ ├── delete-test-plan-variable.ts │ │ ├── delete-url-replacement.ts │ │ ├── describe-result.ts │ │ ├── describe-scenario.ts │ │ ├── execute-scenarios.ts │ │ ├── execute-schedule.ts │ │ ├── get-credit-usage.ts │ │ ├── get-project-info.ts │ │ ├── list-access-points.ts │ │ ├── list-capabilities.ts │ │ ├── list-results.ts │ │ ├── list-scenarios.ts │ │ ├── list-test-plan-variable.ts │ │ ├── list-url-replacements.ts │ │ ├── update-test-plan-variable.ts │ │ └── update-url-replacement.ts │ │ ├── auth │ │ └── login.ts │ │ └── test │ │ ├── run.ts │ │ └── wait.ts ├── config.ts └── index.ts └── tsconfig.json /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/typescript-node/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 4 | # see https://github.com/devcontainers/images/tree/main/src/typescript-node 5 | ARG VARIANT="22-bullseye" 6 | FROM mcr.microsoft.com/devcontainers/typescript-node:${VARIANT} 7 | # [Optional] Uncomment this section to install additional OS packages. 8 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | && apt-get -y install --no-install-recommends p7zip-full nsis default-jre tig 10 | 11 | # [Optional] Uncomment if you want to install an additional version of node using nvm 12 | # ARG EXTRA_NODE_VERSION=10 13 | # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" 14 | 15 | # [Optional] Uncomment if you want to install more global node packages 16 | # RUN su node -c "npm install -g " 17 | 18 | RUN curl https://autify-cli-assets.s3.amazonaws.com/autify-cli/channels/stable/install-standalone.sh | sh 19 | 20 | # To avoid annoying message because this directory is created as root by mounts in devcontainer.json 21 | RUN mkdir -p /home/node/.config && chown node: /home/node/.config 22 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/typescript-node 3 | { 4 | "name": "Node.js & TypeScript", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick a Node version: 22, 20, 18. 8 | // Append -bullseye or -buster to pin to an OS version. 9 | // Use -bullseye variants on local on arm64/Apple Silicon. 10 | "args": { 11 | "VARIANT": "20-bullseye" 12 | } 13 | }, 14 | 15 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 16 | // "forwardPorts": [], 17 | 18 | "workspaceFolder": "/home/node/autify-cli", 19 | 20 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 21 | "remoteUser": "node", 22 | "mounts": [ 23 | "source=${localWorkspaceFolder},target=/home/node/autify-cli,type=bind", 24 | "source=${localWorkspaceFolderBasename}_node_modules,target=/home/node/autify-cli/node_modules,type=volume", 25 | "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/node/.aws,type=bind", 26 | "source=${env:HOME}${env:USERPROFILE}/.config/autify,target=/home/node/.config/autify,type=bind", 27 | "source=${env:HOME}${env:USERPROFILE}/.config/gh,target=/home/node/.config/gh,type=bind" 28 | ], 29 | 30 | // Use 'postCreateCommand' to run commands after the container is created. 31 | "postCreateCommand": "sudo chown -R node:node /home/node/autify-cli/node_modules", 32 | 33 | "features": { 34 | "github-cli": "latest", 35 | "aws-cli": "latest" 36 | }, 37 | "customizations": { 38 | "vscode": { 39 | // Set *default* container specific settings.json values on container create. 40 | "settings": {}, 41 | // Add the IDs of extensions you want installed when the container is created. 42 | "extensions": ["dbaeumer.vscode-eslint"] 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.github/actions/install/action.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | description: Install from S3 3 | 4 | inputs: 5 | target: 6 | description: target 7 | required: true 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - uses: ./.github/actions/setup 13 | - run: npm install 14 | shell: bash 15 | - run: npm run release install ${{ inputs.target }} 16 | shell: bash 17 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup 3 | 4 | inputs: 5 | aws-credentials: 6 | description: AWS credentials 7 | required: false 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - uses: actions/setup-node@v4 13 | with: 14 | cache: npm 15 | registry-url: "https://registry.npmjs.org" 16 | node-version-file: "package.json" 17 | node-version: 20 18 | # https://github.com/npm/cli/issues/7308 19 | - if: runner.os == 'Windows' 20 | run: npm install -g npm@9.9.3 21 | shell: bash 22 | - run: npm install 23 | shell: bash 24 | - uses: aws-actions/configure-aws-credentials@v4 25 | if: ${{ inputs.aws-credentials == 'upload' }} 26 | with: 27 | role-to-assume: arn:aws:iam::434848343351:role/autify-cli-upload-only-role 28 | role-session-name: github-actions 29 | aws-region: us-west-2 30 | - uses: aws-actions/configure-aws-credentials@v4 31 | if: ${{ inputs.aws-credentials == 'main' }} 32 | with: 33 | role-to-assume: arn:aws:iam::434848343351:role/autify-cli-beta-release-role 34 | role-session-name: github-actions 35 | aws-region: us-west-2 36 | - uses: aws-actions/configure-aws-credentials@v4 37 | if: ${{ startsWith(inputs.aws-credentials, 'releases/') || inputs.aws-credentials == 'publish' }} 38 | with: 39 | role-to-assume: arn:aws:iam::434848343351:role/autify-cli-release-role 40 | role-session-name: github-actions 41 | aws-region: us-west-2 42 | -------------------------------------------------------------------------------- /.github/actions/upload/action.yml: -------------------------------------------------------------------------------- 1 | name: Upload 2 | description: Upload to S3 3 | 4 | inputs: 5 | target: 6 | description: target 7 | required: true 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - uses: ./.github/actions/setup 13 | with: 14 | aws-credentials: upload 15 | - if: ${{ inputs.target == 'win' }} 16 | run: sudo apt-get install nsis 17 | shell: bash 18 | - run: npm install 19 | shell: bash 20 | - run: npm run release upload ${{ inputs.target }} 21 | shell: bash 22 | -------------------------------------------------------------------------------- /.github/workflows/bump.yml: -------------------------------------------------------------------------------- 1 | name: Bump 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | bump: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - id: generate-token 11 | uses: tibdex/github-app-token@v2 12 | with: 13 | app_id: ${{ secrets.BOT_APP_ID }} 14 | private_key: ${{ secrets.BOT_PRIVATE_KEY }} 15 | - uses: actions/checkout@v4 16 | with: 17 | token: ${{ steps.generate-token.outputs.token }} 18 | - uses: ./.github/actions/setup 19 | - run: git config user.name "${GITHUB_ACTOR}" 20 | - run: git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" 21 | - run: npm run release bump 22 | -------------------------------------------------------------------------------- /.github/workflows/e2e-tests.yml: -------------------------------------------------------------------------------- 1 | name: E2E tests 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN: 7 | required: true 8 | 9 | permissions: 10 | id-token: write 11 | contents: read 12 | 13 | jobs: 14 | tarball: 15 | strategy: 16 | matrix: 17 | include: 18 | - target: darwin-arm64 19 | os: macos-latest-xlarge 20 | fail-fast: false 21 | 22 | runs-on: ${{ matrix.os }} 23 | 24 | steps: 25 | - run: git config --global core.longpaths true 26 | - uses: actions/checkout@v4 27 | with: 28 | ref: ${{ github.event.pull_request.head.sha }} 29 | - uses: ./.github/actions/install 30 | with: 31 | target: ${{ matrix.target }} 32 | - run: npm run test:e2e 33 | env: 34 | AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN: ${{ secrets.AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/integration-tests.yml: -------------------------------------------------------------------------------- 1 | name: Integration tests 2 | 3 | on: 4 | workflow_call: 5 | 6 | permissions: 7 | id-token: write 8 | contents: read 9 | 10 | jobs: 11 | tarball: 12 | strategy: 13 | matrix: 14 | include: 15 | - target: linux-x64 16 | os: ubuntu-latest 17 | - target: win32-x64 18 | os: windows-latest 19 | - target: darwin-x64 20 | os: macos-latest 21 | fail-fast: false 22 | 23 | runs-on: ${{ matrix.os }} 24 | 25 | steps: 26 | - run: git config --global core.longpaths true 27 | - uses: actions/checkout@v4 28 | with: 29 | ref: ${{ github.event.pull_request.head.sha }} 30 | - uses: ./.github/actions/install 31 | with: 32 | target: ${{ matrix.target }} 33 | - run: echo test | autify web auth login 34 | - run: echo test | autify mobile auth login 35 | - run: autify connect client install 36 | env: 37 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 38 | - run: npm run test:integration 39 | 40 | win: 41 | runs-on: windows-latest 42 | 43 | steps: 44 | - run: git config --global core.longpaths true 45 | - uses: actions/checkout@v4 46 | with: 47 | ref: ${{ github.event.pull_request.head.sha }} 48 | - uses: ./.github/actions/install 49 | with: 50 | target: win 51 | - run: echo test | autify web auth login 52 | - run: echo test | autify mobile auth login 53 | - run: autify connect client install 54 | env: 55 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 56 | - run: npm run test:integration 57 | 58 | macos: 59 | runs-on: macos-latest 60 | 61 | steps: 62 | - uses: actions/checkout@v4 63 | with: 64 | ref: ${{ github.event.pull_request.head.sha }} 65 | - uses: ./.github/actions/install 66 | with: 67 | target: macos 68 | - run: echo test | autify web auth login 69 | - run: echo test | autify mobile auth login 70 | - run: autify connect client install 71 | env: 72 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 73 | - run: npm run test:integration 74 | 75 | standalone-shell: 76 | strategy: 77 | matrix: 78 | os: 79 | - ubuntu-latest 80 | - macos-latest 81 | # - windows-latest 82 | fail-fast: false 83 | 84 | runs-on: ${{ matrix.os }} 85 | 86 | steps: 87 | - uses: actions/checkout@v4 88 | with: 89 | ref: ${{ github.event.pull_request.head.sha }} 90 | - uses: ./.github/actions/install 91 | with: 92 | target: standalone-shell 93 | - run: echo test | autify web auth login 94 | - run: echo test | autify mobile auth login 95 | - run: autify connect client install 96 | env: 97 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 98 | - run: npm run test:integration 99 | 100 | cicd-shell: 101 | strategy: 102 | matrix: 103 | os: 104 | - ubuntu-latest 105 | - macos-latest 106 | - windows-latest 107 | fail-fast: false 108 | 109 | runs-on: ${{ matrix.os }} 110 | 111 | steps: 112 | - run: git config --global core.longpaths true 113 | - uses: actions/checkout@v4 114 | with: 115 | ref: ${{ github.event.pull_request.head.sha }} 116 | - uses: ./.github/actions/install 117 | with: 118 | target: cicd-shell 119 | env: 120 | AUTIFY_CLI_INTEGRATION_TEST_INSTALL: 1 121 | - run: echo test | autify web auth login 122 | shell: bash 123 | - run: echo test | autify mobile auth login 124 | shell: bash 125 | - run: autify connect client install 126 | shell: bash 127 | env: 128 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 129 | - run: autify-cli-integration-test 130 | shell: bash 131 | 132 | brew: 133 | strategy: 134 | matrix: 135 | include: 136 | # `brew` has been removed from ubuntu-latest? 137 | # - os: ubuntu-latest 138 | - os: macos-latest 139 | fail-fast: false 140 | 141 | runs-on: ${{ matrix.os }} 142 | 143 | steps: 144 | - uses: actions/checkout@v4 145 | with: 146 | ref: ${{ github.event.pull_request.head.sha }} 147 | - uses: ./.github/actions/install 148 | with: 149 | target: brew 150 | - run: echo test | autify web auth login 151 | - run: echo test | autify mobile auth login 152 | - run: autify connect client install 153 | env: 154 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 155 | - run: npm run test:integration 156 | 157 | npm: 158 | runs-on: ubuntu-latest 159 | 160 | steps: 161 | - uses: actions/checkout@v4 162 | with: 163 | ref: ${{ github.event.pull_request.head.sha }} 164 | - uses: ./.github/actions/install 165 | with: 166 | target: npm 167 | - run: echo test | autify web auth login 168 | - run: echo test | autify mobile auth login 169 | - run: autify connect client install 170 | env: 171 | AUTIFY_CONNECT_CLIENT_MODE: "fake" 172 | - run: autify-cli-integration-test 173 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | branch: 7 | type: string 8 | required: true 9 | 10 | jobs: 11 | branch: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | ref: ${{ github.event.pull_request.head.sha }} 18 | - uses: ./.github/actions/setup 19 | - run: npx prettier -c . 20 | - run: npm run lint 21 | -------------------------------------------------------------------------------- /.github/workflows/merge-bump.yml: -------------------------------------------------------------------------------- 1 | name: Merge bump branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - bump/beta/* 7 | - bump/rc/* 8 | 9 | jobs: 10 | approved: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - run: echo "Approved!" 14 | 15 | merge-bump: 16 | runs-on: ubuntu-latest 17 | needs: [approved] 18 | steps: 19 | - id: generate-token 20 | uses: tibdex/github-app-token@v2 21 | with: 22 | app_id: ${{ secrets.BOT_APP_ID }} 23 | private_key: ${{ secrets.BOT_PRIVATE_KEY }} 24 | - uses: actions/checkout@v4 25 | with: 26 | token: ${{ steps.generate-token.outputs.token }} 27 | fetch-depth: 0 28 | - uses: ./.github/actions/setup 29 | - run: npm run release merge-bump 30 | env: 31 | GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} 32 | -------------------------------------------------------------------------------- /.github/workflows/pr-bump.yml: -------------------------------------------------------------------------------- 1 | name: Pull request bump branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - bump/stable/* 7 | 8 | jobs: 9 | pr-bump: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - id: generate-token 13 | uses: tibdex/github-app-token@v2 14 | with: 15 | app_id: ${{ secrets.BOT_APP_ID }} 16 | private_key: ${{ secrets.BOT_PRIVATE_KEY }} 17 | - uses: actions/checkout@v4 18 | with: 19 | token: ${{ steps.generate-token.outputs.token }} 20 | fetch-depth: 0 21 | - uses: ./.github/actions/setup 22 | - run: npm run release pr-bump 23 | env: 24 | GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }} 25 | -------------------------------------------------------------------------------- /.github/workflows/promote.yml: -------------------------------------------------------------------------------- 1 | name: Promote 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | BOT_APP_ID: 7 | required: true 8 | BOT_PRIVATE_KEY: 9 | required: true 10 | 11 | permissions: 12 | id-token: write 13 | contents: write 14 | 15 | jobs: 16 | channel: 17 | runs-on: "ubuntu-latest" 18 | 19 | steps: 20 | - id: generate-token 21 | uses: tibdex/github-app-token@v2 22 | with: 23 | app_id: ${{ secrets.BOT_APP_ID }} 24 | private_key: ${{ secrets.BOT_PRIVATE_KEY }} 25 | - uses: actions/checkout@v4 26 | with: 27 | token: ${{ steps.generate-token.outputs.token }} 28 | - uses: ./.github/actions/setup 29 | with: 30 | aws-credentials: ${{ github.ref_name }} 31 | - run: npm run release promote 32 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | permissions: 8 | id-token: write 9 | contents: write 10 | 11 | jobs: 12 | channel: 13 | runs-on: "ubuntu-latest" 14 | 15 | steps: 16 | - id: generate-token 17 | uses: tibdex/github-app-token@v2 18 | with: 19 | app_id: ${{ secrets.BOT_APP_ID }} 20 | private_key: ${{ secrets.BOT_PRIVATE_KEY }} 21 | - uses: actions/checkout@v4 22 | with: 23 | token: ${{ steps.generate-token.outputs.token }} 24 | - uses: actions/checkout@v4 25 | with: 26 | repository: autifyhq/homebrew-tap 27 | path: homebrew-tap 28 | token: ${{ steps.generate-token.outputs.token }} 29 | - uses: ./.github/actions/setup 30 | with: 31 | aws-credentials: publish 32 | - run: git config --global user.name "${GITHUB_ACTOR}" 33 | - run: git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com" 34 | - run: npm run release publish 35 | env: 36 | NODE_AUTH_TOKEN: ${{secrets.NPM_RELEASE_TOKEN}} 37 | -------------------------------------------------------------------------------- /.github/workflows/pull-requests.yml: -------------------------------------------------------------------------------- 1 | name: Pull requests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - releases/* 8 | 9 | jobs: 10 | validate: 11 | uses: ./.github/workflows/validate.yml 12 | with: 13 | branch: ${{ github.base_ref }} 14 | 15 | lint: 16 | uses: ./.github/workflows/lint.yml 17 | with: 18 | branch: ${{ github.base_ref }} 19 | 20 | upload: 21 | needs: [validate] 22 | uses: ./.github/workflows/upload.yml 23 | 24 | integration-tests: 25 | needs: [upload] 26 | uses: ./.github/workflows/integration-tests.yml 27 | 28 | e2e-tests: 29 | needs: [upload] 30 | uses: ./.github/workflows/e2e-tests.yml 31 | secrets: 32 | AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN: ${{ secrets.AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN }} 33 | 34 | github-actions-tests: 35 | needs: [upload] 36 | uses: ./.github/workflows/github-actions-tests.yml 37 | 38 | approved: 39 | needs: [integration-tests, e2e-tests, github-actions-tests] 40 | if: ${{ always() }} 41 | runs-on: ubuntu-latest 42 | steps: 43 | - run: | 44 | if [[ "${{ needs.integration-tests.result }}" == "success" && "${{ needs.e2e-tests.result }}" == "success" ]]; then 45 | echo "Approved!" 46 | else 47 | echo "Not approved!" 48 | exit 1 49 | fi 50 | -------------------------------------------------------------------------------- /.github/workflows/push-branch.yml: -------------------------------------------------------------------------------- 1 | name: Push branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - releases/* 8 | 9 | jobs: 10 | validate: 11 | uses: ./.github/workflows/validate.yml 12 | with: 13 | branch: ${{ github.ref_name }} 14 | 15 | upload: 16 | needs: [validate] 17 | uses: ./.github/workflows/upload.yml 18 | 19 | integration-tests: 20 | needs: [upload] 21 | uses: ./.github/workflows/integration-tests.yml 22 | 23 | e2e-tests: 24 | needs: [upload] 25 | uses: ./.github/workflows/e2e-tests.yml 26 | secrets: 27 | AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN: ${{ secrets.AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN }} 28 | 29 | github-actions-tests: 30 | needs: [upload] 31 | uses: ./.github/workflows/github-actions-tests.yml 32 | 33 | promote: 34 | needs: [integration-tests, e2e-tests, github-actions-tests] 35 | uses: ./.github/workflows/promote.yml 36 | secrets: 37 | BOT_APP_ID: ${{ secrets.BOT_APP_ID }} 38 | BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }} 39 | -------------------------------------------------------------------------------- /.github/workflows/rollback.yml: -------------------------------------------------------------------------------- 1 | name: Rollback 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | bump: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: ./.github/actions/setup 12 | with: 13 | aws-credentials: releases/* 14 | - run: npm install 15 | - run: npm run release rollback 16 | -------------------------------------------------------------------------------- /.github/workflows/upload.yml: -------------------------------------------------------------------------------- 1 | name: Upload 2 | 3 | on: 4 | workflow_call: 5 | 6 | permissions: 7 | id-token: write 8 | contents: read 9 | 10 | jobs: 11 | tarball: 12 | strategy: 13 | matrix: 14 | target: 15 | [ 16 | "darwin-arm64", 17 | "darwin-x64", 18 | "linux-arm", 19 | "linux-arm64", 20 | "linux-x64", 21 | "win32-x86", 22 | "win32-x64", 23 | ] 24 | fail-fast: false 25 | 26 | runs-on: "ubuntu-latest" 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | ref: ${{ github.event.pull_request.head.sha }} 32 | - uses: ./.github/actions/upload 33 | with: 34 | target: ${{ matrix.target }} 35 | 36 | win: 37 | runs-on: "ubuntu-latest" 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | with: 42 | ref: ${{ github.event.pull_request.head.sha }} 43 | - uses: ./.github/actions/upload 44 | with: 45 | target: win 46 | 47 | macos: 48 | runs-on: "macos-latest" 49 | 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | ref: ${{ github.event.pull_request.head.sha }} 54 | - uses: ./.github/actions/upload 55 | with: 56 | target: macos 57 | 58 | npm: 59 | runs-on: "ubuntu-latest" 60 | 61 | steps: 62 | - uses: actions/checkout@v4 63 | with: 64 | ref: ${{ github.event.pull_request.head.sha }} 65 | - uses: ./.github/actions/upload 66 | with: 67 | target: npm 68 | 69 | shell: 70 | runs-on: "ubuntu-latest" 71 | 72 | steps: 73 | - uses: actions/checkout@v4 74 | with: 75 | ref: ${{ github.event.pull_request.head.sha }} 76 | - uses: ./.github/actions/upload 77 | with: 78 | target: shell 79 | 80 | # Not supported yet: https://github.com/oclif/oclif/issues/887 81 | #deb: 82 | # runs-on: 'ubuntu-latest' 83 | # 84 | # steps: 85 | # - uses: actions/checkout@v3 86 | # with: 87 | # ref: ${{ github.event.pull_request.head.sha }} 88 | # - uses: ./.github/actions/upload 89 | # with: 90 | # target: deb 91 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | branch: 7 | type: string 8 | required: true 9 | 10 | jobs: 11 | branch: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | ref: ${{ github.event.pull_request.head.sha }} 18 | - uses: ./.github/actions/setup 19 | - run: npm run release validate ${{ inputs.branch }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | /lib 6 | /tmp 7 | /yarn.lock 8 | node_modules 9 | oclif.manifest.json 10 | *.tgz 11 | ios.app* 12 | ios.ipa* 13 | android.apk* 14 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": ["test/helpers/init.js", "ts-node/register"], 3 | "watch-extensions": ["ts"], 4 | "recursive": true, 5 | "reporter": "spec", 6 | "timeout": 60000 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /tmp 3 | /integration-test/dist -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Autify Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /autify-cli.rb: -------------------------------------------------------------------------------- 1 | # CAVEAT: Do not modify this file manually. 2 | # This file is auto-generated by GitHub Actions of autify-cli. 3 | # Edit https://github.com/autifyhq/autify-cli/blob/main/autify-cli.rb instead. 4 | 5 | class AutifyCli < Formula 6 | desc "Autify Command-Line Interface (CLI)" 7 | homepage "https://github.com/autifyhq/autify-cli" 8 | url "https://github.com/autifyhq/autify-cli", using: :git, revision: "" 9 | version "" 10 | # sha256 "" 11 | license "MIT" 12 | 13 | def install 14 | system "curl #{taball_url} | tar xz" 15 | inreplace "autify/bin/autify", /^CLIENT_HOME=/, "export AUTIFY_OCLIF_CLIENT_HOME=#{lib}/client\nCLIENT_HOME=" 16 | libexec.install Dir["autify/*"] 17 | bin.install_symlink libexec/"bin"/"autify" 18 | end 19 | 20 | test do 21 | assert_match "autify-cli/#{version}", shell_output("#{bin}/autify --version") 22 | end 23 | 24 | private 25 | 26 | def taball_url 27 | package = JSON.parse(File.read("./package.json"), symbolize_names: true) 28 | raise "Version mismatch: #{package[:version]}" if package[:version] != version 29 | 30 | bucket = package.dig(:oclif, :update, :s3, :bucket) 31 | folder = package.dig(:oclif, :update, :s3, :folder) 32 | sha = `git rev-parse --short HEAD`.strip 33 | uname_os = `uname`.strip 34 | os = case uname_os 35 | when /darwin/i 36 | "darwin" 37 | when /linux/i 38 | "linux" 39 | else 40 | raise "Unsupported os: #{uname_os}" 41 | end 42 | uname_arch = `uname -m`.strip 43 | arch = case uname_arch 44 | when /x86_64/i 45 | "x64" 46 | when /aarch/i 47 | "arm" 48 | when /arm64/i 49 | "arm64" 50 | else 51 | raise "Unsupported arch: #{uname_arch}" 52 | end 53 | "https://#{bucket}.s3.amazonaws.com/#{folder}/versions/#{version}/#{sha}/autify-v#{version}-#{sha}-#{os}-#{arch}.tar.gz" 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const oclif = require("@oclif/core"); 4 | 5 | const path = require("path"); 6 | const project = path.join(__dirname, "..", "tsconfig.json"); 7 | 8 | // In dev mode -> use ts-node and dev plugins 9 | // When `bin/dev` is invoked in integration-test it'll be "test" 10 | process.env.NODE_ENV ||= "development"; 11 | 12 | require("ts-node").register({ project }); 13 | 14 | // In dev mode, always show stack traces 15 | // In test mode, stack traces shouldn't appeared in snapshot 16 | if (process.env.NODE_ENV === "development") { 17 | oclif.settings.debug = true; 18 | } 19 | 20 | // Start the CLI 21 | oclif 22 | .run() 23 | .then(oclif.flush) 24 | .catch((error) => { 25 | const oclifHandler = oclif.Errors.handle; 26 | if (error.response?.data) 27 | error.message = `${error.message}: ${JSON.stringify( 28 | error.response.data 29 | )}`; 30 | return oclifHandler(error); 31 | }); 32 | -------------------------------------------------------------------------------- /bin/dev.cmd: -------------------------------------------------------------------------------- 1 | @ECHO off 2 | GOTO start 3 | :find_dp0 4 | SET dp0=%~dp0 5 | EXIT /b 6 | :start 7 | SETLOCAL 8 | CALL :find_dp0 9 | 10 | IF EXIST "%dp0%\node.exe" ( 11 | SET "_prog=%dp0%\node.exe" 12 | ) ELSE ( 13 | SET "_prog=node" 14 | SET PATHEXT=%PATHEXT:;.JS;=;% 15 | ) 16 | 17 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\dev" %* 18 | -------------------------------------------------------------------------------- /bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const oclif = require("@oclif/core"); 4 | 5 | oclif 6 | .run() 7 | .then(require("@oclif/core/flush")) 8 | .catch((error) => { 9 | const { handle: oclifHandler } = require("@oclif/core/handle"); 10 | if (error.response?.data) 11 | error.message = `${error.message}: ${JSON.stringify( 12 | error.response.data 13 | )}`; 14 | return oclifHandler(error); 15 | }); 16 | -------------------------------------------------------------------------------- /bin/run.cmd: -------------------------------------------------------------------------------- 1 | @ECHO off 2 | GOTO start 3 | :find_dp0 4 | SET dp0=%~dp0 5 | EXIT /b 6 | :start 7 | SETLOCAL 8 | CALL :find_dp0 9 | 10 | IF EXIST "%dp0%\node.exe" ( 11 | SET "_prog=%dp0%\node.exe" 12 | ) ELSE ( 13 | SET "_prog=node" 14 | SET PATHEXT=%PATHEXT:;.JS;=;% 15 | ) 16 | 17 | endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\run" %* 18 | -------------------------------------------------------------------------------- /e2e-test/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/prefer-module */ 2 | /** @type {import('ts-jest').JestConfigWithTsJest} **/ 3 | module.exports = { 4 | testEnvironment: "node", 5 | transform: { 6 | "^.+\\.tsx?$": ["ts-jest", {}], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /e2e-test/mobile/delay.ts: -------------------------------------------------------------------------------- 1 | export const delay = (ms: number) => 2 | new Promise((resolve) => { 3 | setTimeout(resolve, ms); 4 | }); 5 | -------------------------------------------------------------------------------- /e2e-test/mobile/get-autify-cli-path.ts: -------------------------------------------------------------------------------- 1 | // Autify CLI is expected to be globally installed through the install workflow on CI but you can specify a specific 2 | // binary when you run jest locally. 3 | export const getAutifyCliPath = (): string => 4 | process.env.AUTIFY_CLI_PATH ?? "autify"; 5 | -------------------------------------------------------------------------------- /e2e-test/mobile/interact-with-process.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from "node:child_process"; 2 | import { env } from "node:process"; 3 | 4 | type Interaction = 5 | | { 6 | type: "expect"; 7 | regex: RegExp; 8 | } 9 | | { 10 | type: "question"; 11 | regex: RegExp; 12 | answer: string; 13 | }; 14 | 15 | export const interactWithProcess = async ( 16 | binaryPath: string, 17 | argv: string[], 18 | interactions: Interaction[], 19 | extraEnv: { [key: string]: string } = {} 20 | ): Promise => { 21 | const child = spawn(binaryPath, argv, { 22 | env: { 23 | ...env, 24 | ...extraEnv, 25 | }, 26 | }); 27 | 28 | const interactionWithResults = interactions.map((interaction) => ({ 29 | ...interaction, 30 | result: false, 31 | })); 32 | 33 | for await (const interaction of interactionWithResults) { 34 | let text = ""; 35 | for await (const data of child.stdout.iterator({ 36 | destroyOnReturn: false, 37 | })) { 38 | process.stdout.write(data); 39 | text += data.toString(); 40 | if (interaction.regex.test(text)) { 41 | if (interaction.type === "question") { 42 | child.stdin.write(interaction.answer); 43 | } 44 | 45 | interaction.result = true; 46 | break; 47 | } 48 | } 49 | } 50 | 51 | for (const interaction of interactionWithResults) { 52 | if (!interaction.result) { 53 | throw new Error(`Didn't get expected output: ${interaction.regex}`); 54 | } 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /e2e-test/mobile/local-execution-flow.test.ts: -------------------------------------------------------------------------------- 1 | import { jest, test } from "@jest/globals"; 2 | import execa from "execa"; 3 | 4 | import { delay } from "./delay"; 5 | import { getAutifyCliPath } from "./get-autify-cli-path"; 6 | import { interactWithProcess } from "./interact-with-process"; 7 | import { terminateStartProcess } from "./terminate-start-process"; 8 | import { pickupOneSimulator } from "./pickup-one-simulator"; 9 | 10 | const BUILD_ID = "rQuzJw"; 11 | const WORKSPACE_ID = "2WxFvm"; 12 | const TEAM_ID = "7XV69VPSD3"; 13 | const TEST_PLAN_URL = 14 | "https://mobile-app.autify.com/projects/2WxFvm/test_plans/kYtaqp"; 15 | const TIMEOUT_SECONDS = 20 * 60; // 20 minutes 16 | 17 | jest.setTimeout(TIMEOUT_SECONDS * 1000); 18 | 19 | test("NoCode Mobile local device test execution flow", async () => { 20 | const { stdout: autifyCliVersion } = await execa("autify", ["--version"]); 21 | console.log("AUTIFY CLI version:", autifyCliVersion); 22 | 23 | const simulator = await pickupOneSimulator(); 24 | if (!simulator) { 25 | throw new Error("No available iPhone simulator found"); 26 | } 27 | 28 | console.log(`Simulator: ${simulator.name}(${simulator.udid})`); 29 | 30 | // Install mobilelink first to run the `config clean` command 31 | await interactWithProcess( 32 | getAutifyCliPath(), 33 | ["mobile", "link", "install"], 34 | [ 35 | { 36 | type: "expect", 37 | regex: /Successfully installed Mobile Link/, 38 | }, 39 | ] 40 | ); 41 | 42 | const { stdout: mobileLinkVersion } = await execa("autify", [ 43 | "mobile", 44 | "link", 45 | "exec", 46 | "--version", 47 | ]); 48 | console.log("MobileLink version:", mobileLinkVersion); 49 | 50 | await interactWithProcess( 51 | getAutifyCliPath(), 52 | ["mobile", "link", "exec", "config", "clean"], 53 | [ 54 | { 55 | type: "expect", 56 | regex: /The configuration has been cleaned/, 57 | }, 58 | ] 59 | ); 60 | 61 | await interactWithProcess( 62 | getAutifyCliPath(), 63 | ["mobile", "auth", "login"], 64 | [ 65 | { 66 | type: "question", 67 | regex: /Enter Access Token/, 68 | answer: `${process.env.AUTIFY_CLI_E2E_AUTIFY_MOBILE_ACCESS_TOKEN}\n`, 69 | }, 70 | ] 71 | ); 72 | 73 | await interactWithProcess( 74 | getAutifyCliPath(), 75 | ["mobile", "link", "setup"], 76 | [ 77 | { 78 | type: "question", 79 | regex: 80 | /Enter the workspace ID of Nocode Mobile to use by default.*\n>/s, 81 | answer: `${WORKSPACE_ID}\n`, 82 | }, 83 | { 84 | type: "question", 85 | regex: /Would you like to send information about test execution.*\n>/s, 86 | answer: "Yes\n", 87 | }, 88 | { 89 | type: "question", 90 | regex: /Enter the Team ID of the Apple Developer Program.*\n>/s, 91 | answer: `${TEAM_ID}\n`, 92 | }, 93 | { 94 | type: "question", 95 | regex: /Enter the Signing ID of the Apple Developer Program.*\n>/s, 96 | answer: "\n", 97 | }, 98 | { 99 | type: "question", 100 | regex: 101 | /Enter the Bundle ID of WebDriverAgent to use for testing iOS apps.*\n>/s, 102 | answer: "com.autify.WebDriverAgentRunner\n", 103 | }, 104 | ] 105 | ); 106 | 107 | const startMobileLinkResult = interactWithProcess( 108 | getAutifyCliPath(), 109 | ["mobile", "link", "start"], 110 | [ 111 | { 112 | type: "expect", 113 | regex: /Exiting this command with the same exit code\(0\)/, 114 | }, 115 | ], 116 | { 117 | VERBOSE: "true", 118 | } 119 | ); 120 | 121 | const runTest = async () => { 122 | // Ensure MobileLink gets ready 123 | await delay(20_000); 124 | 125 | try { 126 | await interactWithProcess( 127 | getAutifyCliPath(), 128 | [ 129 | "mobile", 130 | "test", 131 | "run", 132 | "--build-id", 133 | BUILD_ID, 134 | "--wait", 135 | "--timeout", 136 | TIMEOUT_SECONDS.toString(), 137 | "--device-ids", 138 | simulator.udid, 139 | TEST_PLAN_URL, 140 | ], 141 | [ 142 | { 143 | type: "expect", 144 | regex: /Test passed!/, 145 | }, 146 | ] 147 | ); 148 | } finally { 149 | terminateStartProcess(); 150 | } 151 | }; 152 | 153 | await Promise.all([startMobileLinkResult, runTest()]); 154 | }); 155 | -------------------------------------------------------------------------------- /e2e-test/mobile/pickup-one-simulator.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | 3 | export type Simulator = { 4 | isAvailable: boolean; 5 | name: string; 6 | udid: string; 7 | }; 8 | 9 | export const pickupOneSimulator = async (): Promise => { 10 | const { stdout } = await execa("xcrun", [ 11 | "simctl", 12 | "list", 13 | "devices", 14 | "--json", 15 | ]); 16 | const { devices } = JSON.parse(stdout); 17 | const simulators = (Object.values(devices).flat() as Simulator[]).filter( 18 | (device) => device.isAvailable && device.name.includes("iPhone") 19 | ); 20 | if (simulators.length === 0) { 21 | return null; 22 | } 23 | 24 | return simulators[0]; 25 | }; 26 | -------------------------------------------------------------------------------- /e2e-test/mobile/terminate-start-process.ts: -------------------------------------------------------------------------------- 1 | import { spawn } from "node:child_process"; 2 | 3 | // The spawned child process is shell process. It's difficult to get the PID of the actuall mobilelink with Node's 4 | // ChildProcess API. We insteadl use pkill command to kill mobilelink. 5 | export const terminateStartProcess = () => { 6 | spawn("pkill", ["-f", "run link start"]); 7 | }; 8 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import js from "@eslint/js"; 2 | import oclif from "eslint-config-oclif"; 3 | import prettier from "eslint-config-prettier"; 4 | 5 | export default [ 6 | { 7 | ignores: [ 8 | "dist", 9 | "bin", 10 | "tmp", 11 | "integration-test/dist", 12 | "integration-test/bin", 13 | "**/*.cjs", 14 | ], 15 | }, 16 | js.configs.recommended, 17 | ...oclif, 18 | prettier, 19 | { 20 | files: ["**/*.ts"], 21 | rules: { 22 | "@typescript-eslint/no-explicit-any": "off", 23 | "@typescript-eslint/no-useless-constructor": "error", 24 | "arrow-body-style": "off", 25 | "mocha/no-global-tests": "off", 26 | "n/no-extraneous-import": [ 27 | "error", 28 | { 29 | allowModules: ["@jest/globals"], 30 | }, 31 | ], 32 | "n/no-process-exit": "off", 33 | "no-useless-constructor": "off", 34 | "object-shorthand": "off", 35 | "perfectionist/sort-classes": "off", 36 | "perfectionist/sort-imports": "off", 37 | "perfectionist/sort-interfaces": "off", 38 | "perfectionist/sort-intersection-types": "off", 39 | "perfectionist/sort-named-imports": "off", 40 | "perfectionist/sort-object-types": "off", 41 | "perfectionist/sort-objects": "off", 42 | "perfectionist/sort-union-types": "off", 43 | "prefer-destructuring": "off", 44 | "unicorn/prefer-event-target": "off", 45 | "unicorn/prefer-string-replace-all": "off", 46 | "unicorn/prefer-top-level-await": "off", 47 | }, 48 | }, 49 | ]; 50 | -------------------------------------------------------------------------------- /install-cicd.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echoerr() { echo "\$@" 1>&2; } 5 | 6 | AUTIFY_CLI_VERSION=REPLACE 7 | AUTIFY_S3_BUCKET=REPLACE 8 | AUTIFY_S3_PREFIX=REPLACE 9 | 10 | WORKSPACE="$(pwd)" 11 | AUTIFY_DIR="$WORKSPACE/autify" 12 | AUTIFY_PATH="$AUTIFY_DIR/path" # Export PATHs to use the installed commands. 13 | 14 | if [ -n "$AUTIFY_CLI_INSTALL_USE_CACHE" ] && [ -d "$AUTIFY_DIR" ]; then 15 | echo "$AUTIFY_DIR exists. Reuse it as cache." 16 | exit 17 | fi 18 | rm -fr "$AUTIFY_DIR" 19 | mkdir "$AUTIFY_DIR" 20 | 21 | if [ "$(uname)" == "Darwin" ]; then 22 | OS=darwin 23 | elif [ "$(uname -s | cut -c 1-5)" == "Linux" ]; then 24 | OS=linux 25 | elif [ "$(uname -s | cut -c 1-5)" == "MINGW" ]; then 26 | OS=windows 27 | elif [ "$(uname -s | cut -c 1-4)" == "MSYS" ]; then 28 | OS=windows 29 | else 30 | echoerr "Unsupported os: $(uname)" 31 | exit 1 32 | fi 33 | 34 | ARCH="$(uname -m)" 35 | if [ "$ARCH" == "x86_64" ]; then 36 | ARCH=x64 37 | elif [ "$ARCH" == "armv7l" ]; then 38 | ARCH=arm 39 | elif [ "$ARCH" == "aarch64" ]; then 40 | ARCH=arm64 41 | elif [ "$ARCH" == "arm64" ]; then 42 | ARCH=arm64 43 | else 44 | echoerr "Unsupported arch: $ARCH" 45 | exit 1 46 | fi 47 | 48 | if [ "$OS" == "windows" ]; then 49 | URL="https://$AUTIFY_S3_BUCKET.s3.amazonaws.com/$AUTIFY_S3_PREFIX-$ARCH.exe" 50 | echo "Installing CLI from $URL" 51 | EXE_FILE="$AUTIFY_DIR/installer.exe" 52 | curl "$URL" > "$EXE_FILE" 53 | MSYS_NO_PATHCONV=1 cmd.exe /C "$(cygpath -w "$EXE_FILE") /S /D=$(cygpath -w "$AUTIFY_DIR")" 54 | else 55 | mkdir "$AUTIFY_DIR/bin" 56 | mkdir "$AUTIFY_DIR/lib" 57 | cd "$AUTIFY_DIR/lib" 58 | if [ "$(command -v xz)" ]; then 59 | TAR_EXT="tar.xz" 60 | TAR_ARGS="xJ" 61 | else 62 | TAR_EXT="tar.gz" 63 | TAR_ARGS="xz" 64 | fi 65 | URL="https://$AUTIFY_S3_BUCKET.s3.amazonaws.com/$AUTIFY_S3_PREFIX-$OS-$ARCH.$TAR_EXT" 66 | echo "Installing CLI from $URL" 67 | if [ "$(command -v curl)" ]; then 68 | curl "$URL" | tar "$TAR_ARGS" 69 | else 70 | wget -O- "$URL" | tar "$TAR_ARGS" 71 | fi 72 | 73 | ln -s "$AUTIFY_DIR/lib/autify/bin/autify" "$AUTIFY_DIR/bin/autify" 74 | fi 75 | 76 | cd "$WORKSPACE" 77 | "$AUTIFY_DIR/bin/autify" --version 78 | echo "$AUTIFY_DIR/bin" >> "$AUTIFY_PATH" 79 | 80 | if [ -n "$AUTIFY_CLI_INTEGRATION_TEST_INSTALL" ]; then 81 | file_prefix=$(basename "$AUTIFY_S3_PREFIX") 82 | dir_prefix=$(dirname "$AUTIFY_S3_PREFIX") 83 | if [ "$file_prefix" == "autify" ]; then 84 | # channel 85 | package="autifyhq-autify-cli-integration-test.tgz" 86 | else 87 | # version 88 | package="autifyhq-autify-cli-integration-test-$AUTIFY_CLI_VERSION.tgz" 89 | fi 90 | package_url="https://$AUTIFY_S3_BUCKET.s3.amazonaws.com/${dir_prefix}/${package}" 91 | 92 | cd "$AUTIFY_DIR" 93 | echo "Installing autify-cli-integration-test package from $package_url" 94 | npm install "$package_url" --prefix "$AUTIFY_DIR" 95 | echo "$AUTIFY_DIR/node_modules/.bin" >> "$AUTIFY_PATH" 96 | fi 97 | -------------------------------------------------------------------------------- /install-standalone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | { 3 | set -e 4 | SUDO='' 5 | if [ "$(id -u)" != "0" ]; then 6 | SUDO='sudo' 7 | echo "This script requires superuser access." 8 | echo "You will be prompted for your password by sudo." 9 | # clear any previous sudo permission 10 | sudo -k 11 | fi 12 | 13 | # run inside sudo 14 | $SUDO bash <