# Triggers after the layered build has finished, taking the artifact and running cypress on it
name: Cypress End to End Tests
on:
    workflow_run:
        workflows: ["Element Web - Build"]
        types:
            - completed
concurrency:
    group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.ref }}
    cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
jobs:
    prepare:
        name: Prepare
        if: github.event.workflow_run.conclusion == 'success'
        runs-on: ubuntu-latest
        permissions:
            actions: read
            issues: read
            statuses: write
            pull-requests: read
        outputs:
            uuid: ${{ steps.uuid.outputs.value }}
            pr_id: ${{ steps.prdetails.outputs.pr_id }}
            commit_message: ${{ steps.commit.outputs.message }}
            commit_author: ${{ steps.commit.outputs.author }}
            commit_email: ${{ steps.commit.outputs.email }}
            percy_enable: ${{ steps.percy.outputs.value || '1' }}
            testrail_enable: ${{ steps.testrail.outputs.value || '1' }}
        steps:
            # We create the status here and then update it to success/failure in the `report` stage
            # This provides an easy link to this workflow_run from the PR before Cypress is done.
            - uses: Sibz/github-status-action@v1
              with:
                  authToken: ${{ secrets.GITHUB_TOKEN }}
                  state: pending
                  context: ${{ github.workflow }} / cypress (${{ github.event.workflow_run.event }} => ${{ github.event_name }})
                  sha: ${{ github.event.workflow_run.head_sha }}
                  target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

            - id: prdetails
              if: github.event.workflow_run.event == 'pull_request'
              uses: matrix-org/pr-details-action@v1.2
              with:
                  owner: ${{ github.event.workflow_run.head_repository.owner.login }}
                  branch: ${{ github.event.workflow_run.head_branch }}

            - name: Get commit details
              id: commit
              if: github.event.workflow_run.event == 'pull_request'
              uses: actions/github-script@v6
              with:
                  script: |
                      const response = await github.rest.git.getCommit({
                        owner: context.repo.owner,
                        repo: context.repo.repo,
                        commit_sha: "${{ github.event.workflow_run.head_sha }}",
                      });
                      core.setOutput("message", response.data.message);
                      core.setOutput("author", response.data.author.name);
                      core.setOutput("email", response.data.author.email);

            # Only run Percy when it is demanded or on develop
            - name: Disable Percy if not needed
              id: percy
              if: |
                  github.event.workflow_run.event == 'pull_request' &&
                  !contains(fromJSON(steps.prdetails.outputs.data).labels.*.name, 'X-Needs-Percy')
              run: echo "::set-output name=value::0"

            # Only run Testrail when it is demanded or on develop
            - name: Disable Testrail if not needed
              id: testrail
              if: |
                  github.event.workflow_run.event == 'pull_request' &&
                  !contains(fromJSON(steps.prdetails.outputs.data).labels.*.name, 'X-Send-Testrail')
              run: echo "::set-output name=value::0"

            - name: Generate unique ID 💎
              id: uuid
              run: echo "::set-output name=value::sha-$GITHUB_SHA-time-$(date +"%s")"

    tests:
        name: "Run Tests"
        needs: prepare
        runs-on: ubuntu-latest
        permissions:
            actions: read
            issues: read
            pull-requests: read
        environment: Cypress
        strategy:
            fail-fast: false
            matrix:
                # Run 4 instances in Parallel
                runner: [1, 2, 3, 4]
        steps:
            - uses: browser-actions/setup-chrome@latest
            - run: echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV

            - uses: tecolicom/actions-use-apt-tools@v1
              with:
                  # Our test suite includes some screenshot tests with unusual diacritics, which are
                  # supposed to be covered by STIXGeneral.
                  tools: fonts-stix

            - uses: actions/checkout@v3
              with:
                  # XXX: We're checking out untrusted code in a secure context
                  # We need to be careful to not trust anything this code outputs/may do
                  # We need to check this out to access the cypress tests which are on the head branch
                  repository: ${{ github.event.workflow_run.head_repository.full_name }}
                  ref: ${{ github.event.workflow_run.head_sha }}
                  persist-credentials: false

            # There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
            # (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
            - name: 📥 Download artifact
              uses: dawidd6/action-download-artifact@v2
              with:
                  run_id: ${{ github.event.workflow_run.id }}
                  name: previewbuild
                  path: webapp

            - name: Run Cypress tests
              uses: cypress-io/github-action@v5.0.2
              with:
                  # The built-in Electron runner seems to grind to a halt trying
                  # to run the tests, so use chrome.
                  browser: "${{ env.BROWSER_PATH }}"
                  start: npx serve -p 8080 webapp
                  wait-on: "http://localhost:8080"
                  record: true
                  parallel: true
                  command-prefix: "yarn percy exec --parallel --"
                  config: '{"reporter":"cypress-multi-reporters", "reporterOptions": { "configFile": "cypress-ci-reporter-config.json" } }'
                  ci-build-id: ${{ needs.prepare.outputs.uuid }}
              env:
                  # pass the Dashboard record key as an environment variable
                  CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

                  # Use existing chromium rather than downloading another
                  PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true

                  # pass GitHub token to allow accurately detecting a build vs a re-run build
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

                  # make Node's os.tmpdir() return something where we actually have permissions
                  TMPDIR: ${{ runner.temp }}

                  # tell Cypress more details about the context of this run
                  COMMIT_INFO_BRANCH: ${{ github.event.workflow_run.head_branch }}
                  COMMIT_INFO_SHA: ${{ github.event.workflow_run.head_sha }}
                  COMMIT_INFO_REMOTE: ${{ github.repositoryUrl }}
                  COMMIT_INFO_MESSAGE: ${{ needs.prepare.outputs.commit_message }}
                  COMMIT_INFO_AUTHOR: ${{ needs.prepare.outputs.commit_author }}
                  COMMIT_INFO_EMAIL: ${{ needs.prepare.outputs.commit_email }}
                  CYPRESS_PULL_REQUEST_ID: ${{ needs.prepare.outputs.pr_id }}
                  CYPRESS_PULL_REQUEST_URL: https://github.com/${{ github.repository }}/pull/${{ needs.prepare.outputs.pr_id }}

                  # pass the Percy token as an environment variable
                  PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
                  PERCY_ENABLE: ${{ needs.prepare.outputs.percy_enable }}
                  PERCY_BROWSER_EXECUTABLE: /usr/bin/chromium-browser
                  # tell Percy more details about the context of this run
                  PERCY_BRANCH: ${{ github.event.workflow_run.head_branch }}
                  PERCY_COMMIT: ${{ github.event.workflow_run.head_sha }}
                  PERCY_PULL_REQUEST: ${{ needs.prepare.outputs.pr_id }}
                  PERCY_PARALLEL_NONCE: ${{ needs.prepare.outputs.uuid }}
                  # We manually finalize the build in the report stage
                  PERCY_PARALLEL_TOTAL: -1

            - name: Upload Artifact
              if: failure()
              uses: actions/upload-artifact@v3
              with:
                  name: cypress-results
                  path: |
                      cypress/screenshots
                      cypress/videos
                      cypress/synapselogs

            - name: Upload reports
              if: always()
              uses: actions/upload-artifact@v2
              with:
                  name: cypress-junit
                  path: cypress/results

    report:
        name: Report results
        needs:
            - prepare
            - tests
        runs-on: ubuntu-latest
        if: always()
        permissions:
            statuses: write
        steps:
            - name: Finalize Percy
              if: needs.prepare.outputs.percy_enable == '1'
              run: npx -p @percy/cli percy build:finalize
              env:
                  PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}
                  PERCY_PARALLEL_NONCE: ${{ needs.prepare.outputs.uuid }}

            - name: Skip Percy required check
              if: needs.prepare.outputs.percy_enable != '1'
              uses: Sibz/github-status-action@v1
              with:
                  authToken: ${{ secrets.GITHUB_TOKEN }}
                  state: success
                  description: Percy skipped
                  context: percy/matrix-react-sdk
                  sha: ${{ github.event.workflow_run.head_sha }}
                  target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

            - uses: Sibz/github-status-action@v1
              with:
                  authToken: ${{ secrets.GITHUB_TOKEN }}
                  state: ${{ needs.tests.result == 'success' && 'success' || 'failure' }}
                  context: ${{ github.workflow }} / cypress (${{ github.event.workflow_run.event }} => ${{ github.event_name }})
                  sha: ${{ github.event.workflow_run.head_sha }}
                  target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}

    testrail:
        name: Report results to testrail
        needs:
            - prepare
            - tests
        environment: Testrail
        runs-on: ubuntu-latest
        if: ${{ needs.prepare.outputs.testrail_enable }}
        steps:
            - uses: actions/download-artifact@v3
              with:
                  name: cypress-junit
            - name: Prepare testrail upload config
              id: testrailprep
              env:
                  TESTRAIL_PROJECT_ID: ${{ secrets.TESTRAIL_PROJECT_ID }}
                  TESTRAIL_SUITE_ID: ${{ secrets.TESTRAIL_SUITE_ID }}
                  TEST_ID: ${{ github.run_id }}
                  TESTRAIL_URL: https://elementqa.testrail.io
                  TESTRAIL_USER: ${{ secrets.TESTRAIL_USER }}
                  TESTRAIL_API_KEY: ${{ secrets.TESTRAIL_API_KEY }}
              run: |
                  echo '{"name": "element-web cypress '$TEST_ID'", "suite_id": '$TESTRAIL_SUITE_ID' }' > body.json # TODO add description with more context?
                  RUN_ID=`curl -X POST -d @body.json -u "$TESTRAIL_USER:$TESTRAIL_API_KEY" -H "Content-Type: application/json" "$TESTRAIL_URL/index.php?/api/v2/add_run/$TESTRAIL_PROJECT_ID" | jq '.id'`
                  PROJECT_NAME=`curl -X GET -u "$TESTRAIL_USER:$TESTRAIL_API_KEY" -H "Content-Type: application/json" "$TESTRAIL_URL/index.php?/api/v2/get_project/$TESTRAIL_PROJECT_ID" | jq '.name'`
                  echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT
                  echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT
            - name: setup python
              uses: actions/setup-python@v4
              with:
                  python-version: "3.11"
            - run: pip install trcli
            - name: Upload junit files
              env:
                  TESTRAIL_PROJECT_ID: ${{ secrets.TESTRAIL_PROJECT_ID }}
                  TESTRAIL_SUITE_ID: ${{ secrets.TESTRAIL_SUITE_ID }}
                  TESTRAIL_URL: https://elementqa.testrail.io
                  TESTRAIL_USER: ${{ secrets.TESTRAIL_USER }}
                  TESTRAIL_API_KEY: ${{ secrets.TESTRAIL_API_KEY }}
                  TESTRAIL_RUN_ID: ${{ steps.testrailprep.outputs.run_id }}
              run: |
                  for file in results-*.xml; do
                      echo "Handling $file"
                      trcli -y -h $TESTRAIL_URL \
                                  --project-id $TESTRAIL_PROJECT_ID \
                                  --project ${{ steps.testrailprep.outputs.project_name }} \
                                  --username $TESTRAIL_USER \
                                  --password $TESTRAIL_API_KEY \
                                  parse_junit \
                                  --run-id $TESTRAIL_RUN_ID \
                                  --suite-id $TESTRAIL_SUITE_ID \
                                  --title "if you see this please check cypress build for run id not being provisioned" \
                                  -f $file || true
                      #  We want to keep uploading what we can; but don't want the failures/red marks when it fails, so we add || true above.
                  done
            - name: Close test run
              id: testrailpost
              if: always()
              env:
                  TESTRAIL_URL: https://elementqa.testrail.io
                  TESTRAIL_USER: ${{ secrets.TESTRAIL_USER }}
                  TESTRAIL_API_KEY: ${{ secrets.TESTRAIL_API_KEY }}
                  TESTRAIL_RUN_ID: ${{ steps.testrailprep.outputs.run_id }}
              run: |
                  CLOSE_RESPONSE=`curl -X POST -d '{}' -u "$TESTRAIL_USER:$TESTRAIL_API_KEY" -H "Content-Type: application/json" "$TESTRAIL_URL/index.php?/api/v2/close_run/$TESTRAIL_RUN_ID"`
                  if [ ! "0" == "`echo $CLOSE_RESPONSE | jq .untested_count`" ] ; then echo "::warning title=Missing Tests::Testrail reported some cypress tests were not run. $CLOSE_RESPONSE"; fi