diff --git a/.github/workflows/e2e-on-release.yml b/.github/workflows/e2e-on-release.yml new file mode 100644 index 000000000..65ac35bf2 --- /dev/null +++ b/.github/workflows/e2e-on-release.yml @@ -0,0 +1,349 @@ +name: Run E2E Tests on Release PRs + +on: + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + if: ${{ startsWith(github.event.pull_request.title, '(release)') }} + outputs: + patient_management_version: ${{steps.versions.outputs.patient_management}} + patient_chart_version: ${{steps.versions.outputs.patient_chart}} + esm_core_version: ${{steps.versions.outputs.esm_core}} + form_builder_version: ${{steps.versions.outputs.form_builder}} + cohort_builder_version: ${{steps.versions.outputs.cohort_builder}} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Checkout to the release commit + run: git checkout 'HEAD^{/\(release\)}' + + - name: Extract version numbers from the spa-build-config.json file + id: versions + run: sh e2e_test_support_files/extract_tag_numbers.sh + + - name: Build and Run Containers + run: docker-compose -f e2e_test_support_files/docker-compose-build.yml up -d + + - name: Wait for the backend to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Commit and export Containers + run: sh e2e_test_support_files/commit_and_export_images.sh + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: e2e_release_env_images + path: e2e_release_env_images.tar.gz + retention-days: 1 + + run-patient-management-e2e-tests: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Create Temporary Directory to Download Docker Images + id: tempdir + run: echo "tmpdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: Download Docker Images + uses: actions/download-artifact@v3 + with: + name: e2e_release_env_images + path: ${{ steps.tempdir.outputs.tmpdir }} + + - name: Load Docker Images + run: | + gzip -d ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar.gz + docker load --input ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar + docker image ls -a + + - name: Spin up an OpenMRS Instance + run: docker-compose up -d + working-directory: e2e_test_support_files + + - name: Checkout to the Repo's Tag + uses: actions/checkout@v4 + with: + repository: openmrs/openmrs-esm-patient-management + ref: refs/tags/v${{ needs.build.outputs.patient_management_version }} + path: e2e_repo + + - name: Copy test environment variables + run: cp example.env .env + working-directory: e2e_repo + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable + working-directory: e2e_repo + + - name: Install Playwright Browsers + run: npx playwright install chromium --with-deps + working-directory: e2e_repo + + - name: Wait for the OpenMRS instance to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Run E2E tests + run: yarn playwright test + working-directory: e2e_repo + + - name: Upload Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: report-patient-management + path: e2e_repo/playwright-report/ + retention-days: 30 + + run-patient-chart-e2e-tests: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Create Temporary Directory to Download Docker Images + id: tempdir + run: echo "tmpdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: Download Docker Images + uses: actions/download-artifact@v3 + with: + name: e2e_release_env_images + path: ${{ steps.tempdir.outputs.tmpdir }} + + - name: Load Docker Images + run: | + gzip -d ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar.gz + docker load --input ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar + docker image ls -a + + - name: Spin up an OpenMRS Instance + run: docker-compose up -d + working-directory: e2e_test_support_files + + - name: Checkout to the Repo's Tag + uses: actions/checkout@v4 + with: + repository: openmrs/openmrs-esm-patient-chart + ref: refs/tags/v${{ needs.build.outputs.patient_chart_version }} + path: e2e_repo + + - name: Copy test environment variables + run: cp example.env .env + working-directory: e2e_repo + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable + working-directory: e2e_repo + + - name: Install Playwright Browsers + run: npx playwright install chromium --with-deps + working-directory: e2e_repo + + - name: Wait for the OpenMRS instance to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Run E2E tests + run: yarn playwright test + working-directory: e2e_repo + + - name: Upload Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: report-patient-chart + path: e2e_repo/playwright-report/ + retention-days: 30 + + run-form-builder-e2e-tests: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Create Temporary Directory to Download Docker Images + id: tempdir + run: echo "tmpdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: Download Docker Images + uses: actions/download-artifact@v3 + with: + name: e2e_release_env_images + path: ${{ steps.tempdir.outputs.tmpdir }} + + - name: Load Docker Images + run: | + gzip -d ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar.gz + docker load --input ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar + docker image ls -a + + - name: Spin up an OpenMRS Instance + run: docker-compose up -d + working-directory: e2e_test_support_files + + - name: Checkout to the Repo's Tag + uses: actions/checkout@v4 + with: + repository: openmrs/openmrs-esm-form-builder + ref: refs/tags/v${{ needs.build.outputs.form_builder_version }} + path: e2e_repo + + - name: Copy test environment variables + run: cp example.env .env + working-directory: e2e_repo + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable + working-directory: e2e_repo + + - name: Install Playwright Browsers + run: npx playwright install chromium --with-deps + working-directory: e2e_repo + + - name: Wait for the OpenMRS instance to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Run E2E tests + run: yarn playwright test + working-directory: e2e_repo + + - name: Upload Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: report-form-builder + path: e2e_repo/playwright-report/ + retention-days: 30 + + run-esm-core-e2e-tests: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Create Temporary Directory to Download Docker Images + id: tempdir + run: echo "tmpdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: Download Docker Images + uses: actions/download-artifact@v3 + with: + name: e2e_release_env_images + path: ${{ steps.tempdir.outputs.tmpdir }} + + - name: Load Docker Images + run: | + gzip -d ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar.gz + docker load --input ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar + docker image ls -a + + - name: Spin up an OpenMRS Instance + run: docker-compose up -d + working-directory: e2e_test_support_files + + - name: Checkout to the Repo's Tag + uses: actions/checkout@v4 + with: + repository: openmrs/openmrs-esm-core + ref: refs/tags/v${{ needs.build.outputs.esm_core_version }} + path: e2e_repo + + - name: Copy test environment variables + run: cp example.env .env + working-directory: e2e_repo + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable + working-directory: e2e_repo + + - name: Install Playwright Browsers + run: npx playwright install chromium --with-deps + working-directory: e2e_repo + + - name: Wait for the OpenMRS instance to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Run E2E tests + run: yarn playwright test + working-directory: e2e_repo + + - name: Upload Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: report-esm-core + path: e2e_repo/playwright-report/ + retention-days: 30 + + run-cohort-builder-e2e-tests: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Create Temporary Directory to Download Docker Images + id: tempdir + run: echo "tmpdir=$(mktemp -d)" >> "$GITHUB_OUTPUT" + + - name: Download Docker Images + uses: actions/download-artifact@v3 + with: + name: e2e_release_env_images + path: ${{ steps.tempdir.outputs.tmpdir }} + + - name: Load Docker Images + run: | + gzip -d ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar.gz + docker load --input ${{ steps.tempdir.outputs.tmpdir }}/e2e_release_env_images.tar + docker image ls -a + + - name: Spin up an OpenMRS Instance + run: docker-compose up -d + working-directory: e2e_test_support_files + + - name: Checkout to the Repo's Tag + uses: actions/checkout@v4 + with: + repository: openmrs/openmrs-esm-cohortbuilder + ref: refs/tags/v${{ needs.build.outputs.cohort_builder_version }} + path: e2e_repo + + - name: Copy test environment variables + run: cp example.env .env + working-directory: e2e_repo + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: yarn install --immutable + working-directory: e2e_repo + + - name: Install Playwright Browsers + run: npx playwright install chromium --with-deps + working-directory: e2e_repo + + - name: Wait for the OpenMRS instance to start + run: while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://localhost:8080/openmrs/login.htm)" != "200" ]]; do sleep 10; done + + - name: Run E2E tests + run: yarn playwright test + working-directory: e2e_repo + + - name: Upload Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: report-cohort-builder + path: e2e_repo/playwright-report/ + retention-days: 30 diff --git a/e2e_test_support_files/README.md b/e2e_test_support_files/README.md new file mode 100644 index 000000000..6afbc3a1a --- /dev/null +++ b/e2e_test_support_files/README.md @@ -0,0 +1,51 @@ +# Run E2E Tests on Release PRs + +This GitHub Actions workflow, named "Run E2E Tests on Release PRs," serves as Quality Gate #4 in the slide +deck [O3 Release QA pipeline](https://docs.google.com/presentation/d/1k3DH74Mz1Afnrgy2MpwR5HQK5vMpVx0pTfN1na62lvI/edit#slide=id.g165af5ac0be_0_24). + +The workflow is designed to automate the end-to-end (E2E) testing process for release pull requests (PRs) +that opened in the format described in +here: [How to Release the O3 RefApp](https://wiki.openmrs.org/display/projects/How+to+Release+the+O3+RefApp) + +The workflow is conditional and **only runs if the pull request title starts with "(release)"**. + +Below is an overview of the key components of the workflow: + + +## Job: build + +This job prepares the test environment, extracts version numbers, builds and runs Docker containers, and uploads +artifacts for use in subsequent E2E testing jobs. + +### Checking out to the release commit + +The reason for the step: + +```yaml +- name: Checkout to the release commit + run: git checkout 'HEAD^{/\(release\)}' +``` + +is to ensure that the workflow checks out to the specific release commit associated with the pull request. This is +necessary because the pull request contain both release commits and revert commits, and the goal is to specifically +target the release commit for further processing. + +## End-to-End Test Jobs + +The workflow includes several end-to-end test jobs, each corresponding to a specific components of O3 (frontend +mono-repos). These jobs are structured similarly and are listed below: + +* `run-patient-management-e2e-tests` +* `run-patient-chart-e2e-tests` +* `run-form-builder-e2e-tests` +* `run-esm-core-e2e-tests` +* `run-cohort-builder-e2e-tests` + +In each "End-to-End Test Job," the workflow first checks out the repository associated with a specific OpenMRS +component. It then downloads Docker images from a previous "build" job, loads these images, and starts an OpenMRS instance. + +### Why Check Out to the Tags? + +The workflow checks out a specific tagged version of the component's repository, the tag is imported from the previous " +build" job. This is necessary because the goal is to perform end-to-end tests on the codebase that corresponds to a +particular release version, rather than the code at the head of the repository. diff --git a/e2e_test_support_files/commit_and_export_images.sh b/e2e_test_support_files/commit_and_export_images.sh new file mode 100644 index 000000000..384699d10 --- /dev/null +++ b/e2e_test_support_files/commit_and_export_images.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +backend_container_id=$(docker ps --filter "name=backend" --format "{{.ID}}") +db_container_id=$(docker ps --filter "name=db" --format "{{.ID}}") +frontend_container_id=$(docker ps --filter "name=frontend" --format "{{.ID}}") +gateway_container_id=$(docker ps --filter "name=gateway" --format "{{.ID}}") + +docker commit $frontend_container_id frontend +docker commit $gateway_container_id gateway +docker commit $backend_container_id backend +docker commit $db_container_id db + +docker save frontend gateway backend db > e2e_release_env_images.tar + +# compress the file (to decrease the upload file size) +gzip -c e2e_release_env_images.tar > e2e_release_env_images.tar.gz diff --git a/e2e_test_support_files/docker-compose-build.yml b/e2e_test_support_files/docker-compose-build.yml new file mode 100644 index 000000000..a4527ce0d --- /dev/null +++ b/e2e_test_support_files/docker-compose-build.yml @@ -0,0 +1,59 @@ +version: "3.7" + +services: + gateway: + build: + context: ../gateway + container_name: gateway + restart: "unless-stopped" + depends_on: + - frontend + - backend + ports: + - "80:80" + + frontend: + build: + context: ../frontend + container_name: frontend + restart: "unless-stopped" + environment: + SPA_PATH: /openmrs/spa + API_URL: /openmrs + SPA_CONFIG_URLS: + SPA_DEFAULT_LOCALE: + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost/" ] + timeout: 5s + depends_on: + - backend + + backend: + build: + context: ../ + container_name: backend + depends_on: + - db + environment: + OMRS_CONFIG_MODULE_WEB_ADMIN: "true" + OMRS_CONFIG_AUTO_UPDATE_DATABASE: "true" + OMRS_CONFIG_CREATE_TABLES: "true" + OMRS_CONFIG_CONNECTION_SERVER: db + OMRS_CONFIG_CONNECTION_DATABASE: openmrs + OMRS_CONFIG_CONNECTION_USERNAME: ${OPENMRS_DB_USER:-openmrs} + OMRS_CONFIG_CONNECTION_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:8080/openmrs" ] + timeout: 5s + + # MariaDB + db: + image: mariadb:10.8.2 + container_name: db + command: "mysqld --character-set-server=utf8 --collation-server=utf8_general_ci --datadir=/openmrs-db" + environment: + MYSQL_DATABASE: openmrs + MYSQL_USER: ${OPENMRS_DB_USER:-openmrs} + MYSQL_PASSWORD: ${OPENMRS_DB_PASSWORD:-openmrs} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-openmrs} + diff --git a/e2e_test_support_files/docker-compose.yml b/e2e_test_support_files/docker-compose.yml new file mode 100644 index 000000000..f36ef81dc --- /dev/null +++ b/e2e_test_support_files/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.7" + +services: + gateway: + image: gateway + restart: "unless-stopped" + depends_on: + - frontend + - backend + ports: + - "8080:80" + + frontend: + image: frontend + restart: "unless-stopped" + depends_on: + - backend + + backend: + image: backend + restart: "unless-stopped" + + db: + image: db + restart: "unless-stopped" + diff --git a/e2e_test_support_files/extract_tag_numbers.sh b/e2e_test_support_files/extract_tag_numbers.sh new file mode 100644 index 000000000..f29decd3e --- /dev/null +++ b/e2e_test_support_files/extract_tag_numbers.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Define a function to extract and output a dependency value +get_repository_tag() { + local file="$1" + local repo_name="$2" + local app="$3" + local value + value=$(awk -F'"' -v app="$app" '$0 ~ app {print $4}' "$file") + echo "$repo_name=$value" +} + +file_path="frontend/spa-build-config.json" + +# Call the function for each Repository with the app as the second argument +get_repository_tag "$file_path" "patient_management" "@openmrs/esm-patient-registration-app" >> "$GITHUB_OUTPUT" +get_repository_tag "$file_path" "patient_chart" "@openmrs/esm-patient-chart-app" >> "$GITHUB_OUTPUT" +get_repository_tag "$file_path" "esm_core" "@openmrs/esm-login-app" >> "$GITHUB_OUTPUT" +get_repository_tag "$file_path" "form_builder" "@openmrs/esm-form-builder-app" >> "$GITHUB_OUTPUT" +get_repository_tag "$file_path" "cohort_builder" "@openmrs/esm-cohort-builder-app" >> "$GITHUB_OUTPUT"