diff --git a/.env b/.env index abcba747..84304209 100644 --- a/.env +++ b/.env @@ -1,11 +1,22 @@ # Global configuration for Ozone HIS -E2E_BASE_URL=https://ozone-dev.mekomsolutions.net -E2E_ODOO_URL=https://erp.ozone-dev.mekomsolutions.net -E2E_SENAITE_URL=https://lims.ozone-dev.mekomsolutions.net -E2E_KEYCLOAK_URL=https://auth.ozone-dev.mekomsolutions.net -E2E_ANALYTICS_URL=https://analytics.ozone-dev.mekomsolutions.net +E2E_BASE_URL_DEMO=https://demo.ozone-his.com +E2E_BASE_URL_DEV=https://ozone-dev.mekomsolutions.net +E2E_BASE_URL_QA=https://ozone-qa.mekomsolutions.net +E2E_ODOO_URL_DEMO=https://erp.demo.ozone-his.com +E2E_ODOO_URL_DEV=https://erp.ozone-dev.mekomsolutions.net +E2E_ODOO_URL_QA=https://erp.ozone-qa.mekomsolutions.net +E2E_SENAITE_URL_DEMO=https://lims.demo.ozone-his.com +E2E_SENAITE_URL_DEV=https://lims.ozone-dev.mekomsolutions.net +E2E_SENAITE_URL_QA=https://lims.ozone-qa.mekomsolutions.net +E2E_KEYCLOAK_URL_DEMO=https://auth.demo.ozone-his.com +E2E_KEYCLOAK_URL_DEV=https://auth.ozone-dev.mekomsolutions.net +E2E_KEYCLOAK_URL_QA=https://auth.ozone-qa.mekomsolutions.net +E2E_ANALYTICS_URL_DEMO=https://analytics.demo.ozone-his.com +E2E_ANALYTICS_URL_DEV=https://analytics.ozone-dev.mekomsolutions.net +E2E_ANALYTICS_URL_QA=https://analytics.ozone-qa.mekomsolutions.net E2E_USER_ADMIN_USERNAME=jdoe E2E_USER_ADMIN_PASSWORD=password FOSS_E2E_USER_ADMIN_USERNAME=admin FOSS_E2E_USER_ADMIN_PASSWORD=Admin123 E2E_RUNNING_ON_OZONE_PRO=true +E2E_TEST_ENVIRONMENT=dev diff --git a/.github/workflows/foss.yml b/.github/workflows/foss.yml index a62529ac..9b428a30 100644 --- a/.github/workflows/foss.yml +++ b/.github/workflows/foss.yml @@ -8,7 +8,7 @@ on: description: 'O3 URL' required: true e2e_odoo_url_input: - description: 'ODOO URL' + description: 'Odoo URL' required: true e2e_senaite_url_input: description: 'SENAITE URL' @@ -38,13 +38,13 @@ jobs: run: yarn install - name: Install Playwright browsers - run: npx playwright install webkit --with-deps + run: npx playwright install chromium --with-deps - name: Run E2E tests env: - E2E_BASE_URL: '${{ github.event.inputs.e2e_base_url_input }}' - E2E_ODOO_URL: '${{ github.event.inputs.e2e_odoo_url_input }}' - E2E_SENAITE_URL: '${{ github.event.inputs.e2e_senaite_url_input }}' + E2E_BASE_URL_DEV: '${{ github.event.inputs.e2e_base_url_input }}' + E2E_ODOO_URL_DEV: '${{ github.event.inputs.e2e_odoo_url_input }}' + E2E_SENAITE_URL_DEV: '${{ github.event.inputs.e2e_senaite_url_input }}' E2E_RUNNING_ON_OZONE_PRO: 'false' run: npm run fossE2ETests diff --git a/.github/workflows/pro.yml b/.github/workflows/pro.yml index 3e178882..fb3d813d 100644 --- a/.github/workflows/pro.yml +++ b/.github/workflows/pro.yml @@ -1,4 +1,4 @@ -name: Ozone PRO E2E Tests +name: Ozone E2E Tests on: schedule: @@ -10,8 +10,7 @@ on: workflow_dispatch: jobs: - testOnPR: - if: ${{ github.event_name == 'pull_request' }} + build: runs-on: ubuntu-latest steps: - name: Checkout repository @@ -35,51 +34,14 @@ jobs: run: yarn install - name: Install Playwright browsers - run: npx playwright install webkit --with-deps - - - name: Run E2E tests - run: npm run proE2ETests - - - name: Upload report - uses: actions/upload-artifact@v3 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 - - testOnPush: - if: ${{ github.event_name == 'push' }} - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - repository: ${{ github.repository }} - - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Cache dependencies - id: cache - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - - - name: Install dependencies - run: yarn install - - - name: Install Playwright browsers - run: npx playwright install webkit --with-deps + run: npx playwright install chromium --with-deps - name: Run E2E tests id: jobStatusPretty run: | npm run proE2ETests + if: ${{ github.event_name != 'pull_request' }} if [[ ${{ job.status }} == "success" ]]; then jobStatusPretty="✅ Passing" else @@ -88,6 +50,7 @@ jobs: echo "jobStatusPretty=$jobStatusPretty" >> "$GITHUB_ENV" - name: Notify Slack + if: ${{ github.event_name != 'pull_request' }} id: slack uses: slackapi/slack-github-action@v1.24.0 with: diff --git a/.github/workflows/runOzoneTestsOnSpecifiedEnv.yml b/.github/workflows/runOzoneTestsOnSpecifiedEnv.yml new file mode 100644 index 00000000..92828ac8 --- /dev/null +++ b/.github/workflows/runOzoneTestsOnSpecifiedEnv.yml @@ -0,0 +1,54 @@ +name: Ozone E2E Tests On Specified Environment + +on: + + workflow_dispatch: + inputs: + environment: + description: 'Choose test environment' + required: true + default: 'dev' + type: choice + options: + - dev + - qa + - demo +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + repository: ${{ github.repository }} + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Cache dependencies + id: cache + uses: actions/cache@v3 + with: + path: '**/node_modules' + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} + + - name: Install dependencies + run: yarn install + + - name: Install Playwright browsers + run: npx playwright install chromium --with-deps + + - name: Run E2E tests + env: + E2E_TEST_ENVIRONMENT: '${{ github.event.inputs.environment }}' + run: npm run proE2ETests + + - name: Upload report + uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/README.md b/README.md index 67e95277..e2b6b238 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ozone E2E Test Suite -[![Ozone PRO E2E Tests](https://github.com/ozone-his/ozone-e2e-pro/actions/workflows/pro.yml/badge.svg)](https://github.com/ozone-his/ozone-e2e-pro/actions/workflows/pro.yml) +[![Ozone E2E Tests](https://github.com/ozone-his/ozone-e2e-pro/actions/workflows/pro.yml/badge.svg)](https://github.com/ozone-his/ozone-e2e-pro/actions/workflows/pro.yml) Welcome to Ozone automated test suite. @@ -75,3 +75,7 @@ The pro.yml workflow is split into two jobs, one that runs upon _Git pull reques The foss.yml workflow contains one job that runs Ozone FOSS specific tests. Note: You need to provide O3, Odoo and SENAITE base URLs at runtime. User Inputs + +The runOzoneTestsOnSpecifiedEnv.yml workflow contains one job that runs Ozone tests. Note: You need to choose test environment at runtime. + +Choose Test Environment diff --git a/e2e/tests/testAnalyticsIntegration.spec.ts b/e2e/tests/testAnalyticsIntegration.spec.ts index 9ca1cbf1..a242a61d 100644 --- a/e2e/tests/testAnalyticsIntegration.spec.ts +++ b/e2e/tests/testAnalyticsIntegration.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import { HomePage } from '../utils/functions/testBase'; import { patientName } from '../utils/functions/testBase'; +import { E2E_BASE_URL, E2E_ANALYTICS_URL } from '../utils/configs/globalSetup'; let homePage: HomePage; @@ -27,13 +28,13 @@ test('Adding an OpenMRS patient syncs patient into patients table in Superset', await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(patientsCountQuery); await homePage.runSQLQuery(); @@ -63,7 +64,7 @@ test('Starting an OpenMRS visit syncs visit into visits table in Superset', asyn // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.goToSuperset(); await expect(page).toHaveURL(/.*superset/); @@ -78,11 +79,11 @@ test('Starting an OpenMRS visit syncs visit into visits table in Superset', asyn await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.startPatientVisit(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(visitsCountQuery); await homePage.runSQLQuery(); @@ -124,7 +125,7 @@ test('Creating an OpenMRS order syncs order into orders table in Superset', asyn // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.startPatientVisit(); await homePage.goToSuperset(); @@ -140,7 +141,7 @@ test('Creating an OpenMRS order syncs order into orders table in Superset', asyn await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.goToLabOrderForm(); await page.getByRole('button', { name: 'Add', exact: true }).click(); @@ -148,7 +149,7 @@ test('Creating an OpenMRS order syncs order into orders table in Superset', asyn await homePage.saveLabOrder(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(ordersCountQuery); await homePage.runSQLQuery(); @@ -190,7 +191,7 @@ test('Adding an OpenMRS encounter syncs encounter into encounters table in Super // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.startPatientVisit(); await homePage.goToSuperset(); @@ -206,7 +207,7 @@ test('Adding an OpenMRS encounter syncs encounter into encounters table in Super await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.goToLabOrderForm(); await page.getByRole('button', { name: 'Add', exact: true }).click(); @@ -214,7 +215,7 @@ test('Adding an OpenMRS encounter syncs encounter into encounters table in Super await homePage.saveLabOrder(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(encountersCountQuery); await homePage.runSQLQuery(); @@ -257,9 +258,9 @@ test('Adding an OpenMRS encounter syncs encounter into encounters table in Super let encounterTypeDescription = await page.getByRole('gridcell', { name: 'Consultation encounter' }); let visitTypeName = await page.getByRole('gridcell', { name: 'Facility Visit' }); let visitTypeUuid = await page.getByRole('gridcell', { name: '7b0f5697-27e3-40c4-8bae-f4049abfb4ed' }); - let formName = await page.getByRole('gridcell', { name: 'Laboratory Test Orders' }); - let formUuid = await page.getByRole('gridcell', { name: '2be26a7a-b2dd-3b16-82e5-81d9c2b5bb7a' }); let formDescription = await page.getByRole('gridcell', { name: 'Simple lab order entry form' }); + let formNameOnDev = await page.getByRole('gridcell', { name: 'Laboratory Test Orders' }); + let formUuidOnDev = await page.getByRole('gridcell', { name: '2be26a7a-b2dd-3b16-82e5-81d9c2b5bb7a' }); let locationName = await page.getByRole('gridcell', { name: 'Inpatient Ward' }).first(); let locationUuid = await page.getByRole('gridcell', { name: 'ba685651-ed3b-4e63-9b35-78893060758a' }); let locationDescription = await page.getByRole('gridcell', { name: 'Inpatient Ward' }).nth(1); @@ -270,9 +271,9 @@ test('Adding an OpenMRS encounter syncs encounter into encounters table in Super await expect(encounterTypeDescription).toHaveText('Consultation encounter'); await expect(visitTypeName).toHaveText('Facility Visit'); await expect(visitTypeUuid).toContainText('7b0f5697-27e3-40c4-8bae-f4049abfb4ed'); - await expect(formName).toHaveText('Laboratory Test Orders'); - await expect(formUuid).toHaveText('2be26a7a-b2dd-3b16-82e5-81d9c2b5bb7a'); await expect(formDescription).toHaveText('Simple lab order entry form'); + await expect(formNameOnDev).toHaveText('Laboratory Test Orders'); + await expect(formUuidOnDev).toHaveText('2be26a7a-b2dd-3b16-82e5-81d9c2b5bb7a'); await expect(locationName).toHaveText('Inpatient Ward'); await expect(locationUuid).toHaveText('ba685651-ed3b-4e63-9b35-78893060758a'); await expect(locationDescription).toHaveText('Inpatient Ward'); @@ -284,7 +285,7 @@ test('Adding an OpenMRS condition syncs condition into conditions table in Super // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.startPatientVisit(); await homePage.goToSuperset(); @@ -300,12 +301,12 @@ test('Adding an OpenMRS condition syncs condition into conditions table in Super await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.addPatientCondition(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(conditionsCountQuery); await homePage.runSQLQuery(); @@ -343,7 +344,7 @@ test('Adding an OpenMRS observation syncs observation into observations table in // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.startPatientVisit(); await homePage.goToSuperset(); @@ -359,12 +360,12 @@ test('Adding an OpenMRS observation syncs observation into observations table in await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.addPatientBiometrics(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(observationsCountQuery); await homePage.runSQLQuery(); @@ -432,7 +433,7 @@ test('Adding an OpenMRS appointment syncs appointment into appointments table in // setup const homePage = new HomePage(page); await homePage.createPatient(); - await homePage.searchPatientId(); + await homePage.searchOpenMRSPatientID(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await homePage.startPatientVisit(); await homePage.goToSuperset(); @@ -449,12 +450,12 @@ test('Adding an OpenMRS appointment syncs appointment into appointments table in await homePage.clearSQLEditor(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.addPatientAppointment(); // verify - await page.goto(`${process.env.E2E_ANALYTICS_URL}/superset/sqllab`); + await page.goto(`${E2E_ANALYTICS_URL}/superset/sqllab`); await homePage.clearSQLEditor(); await page.getByRole('textbox').first().fill(appointmentsCountQuery); await homePage.runSQLQuery(); diff --git a/e2e/tests/testKeycloakIntegration.spec.ts b/e2e/tests/testKeycloakIntegration.spec.ts index a58918d0..7fb1995e 100644 --- a/e2e/tests/testKeycloakIntegration.spec.ts +++ b/e2e/tests/testKeycloakIntegration.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import { HomePage } from '../utils/functions/testBase'; import { randomOpenMRSRoleName } from '../utils/functions/testBase'; +import { E2E_BASE_URL, E2E_KEYCLOAK_URL } from '../utils/configs/globalSetup'; let homePage: HomePage; @@ -13,7 +14,7 @@ test.beforeEach(async ({ page }) => { test('Adding an OpenMRS role syncs the role into Keycloak', async ({ page }) => { // setup - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); const homePage = new HomePage(page); await homePage.addOpenMRSRole(); @@ -38,7 +39,7 @@ test('Adding an OpenMRS role syncs the role into Keycloak', async ({ page }) => test('Updating a synced OpenMRS role updates the corresponding role in Keycloak', async ({ page }) => { // setup - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); const homePage = new HomePage(page); await homePage.addOpenMRSRole(); @@ -57,11 +58,11 @@ test('Updating a synced OpenMRS role updates the corresponding role in Keycloak' await expect(page.getByText('Application: Uses Patient Summary')).toBeTruthy(); await expect(page.getByText('Organizational: Registration Clerk')).toBeTruthy(); await expect(page.getByText('Application: Records Allergies')).toBeTruthy(); - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); await homePage.updateOpenMRSRole(); // verify - await page.goto(`${process.env.E2E_KEYCLOAK_URL}/admin/master/console`); + await page.goto(`${E2E_KEYCLOAK_URL}/admin/master/console`); await homePage.goToClients(); await page.getByRole('link', { name: 'openmrs', exact: true }).click(); await page.getByTestId('rolesTab').click(); @@ -75,7 +76,7 @@ test('Updating a synced OpenMRS role updates the corresponding role in Keycloak' test('Deleting a synced OpenMRS role deletes the corresponding role in Keycloak', async ({ page }) => { // setup - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); const homePage = new HomePage(page); await homePage.addOpenMRSRole(); @@ -97,13 +98,13 @@ test('Deleting a synced OpenMRS role deletes the corresponding role in Keycloak' await homePage.deleteOpenMRSRole(); // verify - await page.goto(`${process.env.E2E_KEYCLOAK_URL}/admin/master/console`); + await page.goto(`${E2E_KEYCLOAK_URL}/admin/master/console`); await homePage.goToClients(); await page.getByRole('link', { name: 'openmrs', exact: true }).click(); await page.getByTestId('rolesTab').click(); const roleName = await page.locator('table tbody tr:nth-child(1) td:nth-child(1) a'); await expect(roleName).not.toHaveText(`${randomOpenMRSRoleName.roleName}`); - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); await homePage.addOpenMRSRole(); }); diff --git a/e2e/tests/testOdooIntegration.spec.ts b/e2e/tests/testOdooIntegration.spec.ts index e7f5756c..805e4a93 100644 --- a/e2e/tests/testOdooIntegration.spec.ts +++ b/e2e/tests/testOdooIntegration.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import { HomePage } from '../utils/functions/testBase'; import { patientName } from '../utils/functions/testBase'; +import { E2E_BASE_URL, E2E_ODOO_URL } from '../utils/configs/globalSetup'; let homePage: HomePage; @@ -56,12 +57,12 @@ test('Editing patient details with a synced lab order edits the corresponding cu await expect(quotation?.includes("Quotation")).toBeTruthy(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.updatePatientDetails(); // verify - await page.goto(`${process.env.E2E_ODOO_URL}`); + await page.goto(`${E2E_ODOO_URL}`); await homePage.searchCustomerInOdoo(); const updatedCustomer = await page.locator("table tbody tr:nth-child(1) td.o_data_cell.o_field_cell.o_list_many2one.o_readonly_modifier.o_required_modifier"); @@ -107,12 +108,12 @@ test('Editing patient details with a synced drug order edits corresponding custo await expect(quotation?.includes("Quotation")).toBeTruthy(); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.updatePatientDetails(); // verify - await page.goto(`${process.env.E2E_ODOO_URL}`); + await page.goto(`${E2E_ODOO_URL}`); await homePage.searchCustomerInOdoo(); const updatedCustomer = await page.locator("table tbody tr:nth-child(1) td.o_data_cell.o_field_cell.o_list_many2one.o_readonly_modifier.o_required_modifier"); @@ -139,12 +140,12 @@ test('Revising a synced drug order edits corresponding quotation line in Odoo', await expect(drugOrderItem).toContainText('Twice daily - 5 Days'); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.editDrugOrder(); // verify - await page.goto(`${process.env.E2E_ODOO_URL}`); + await page.goto(`${E2E_ODOO_URL}`); await homePage.searchCustomerInOdoo(); await page.getByRole('cell', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click(); await expect(drugOrderItem).toContainText('8.0 Tablet'); @@ -170,12 +171,12 @@ test('Discontinuing a synced drug order cancels corresponding quotation line in await expect(drugOrderItem).toHaveText('Aspirin 325mg'); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.discontinueDrugOrder(); // verify - await page.goto(`${process.env.E2E_ODOO_URL}`); + await page.goto(`${E2E_ODOO_URL}`); await homePage.searchCustomerInOdoo(); await expect(customer?.includes(`${patientName.firstName + ' ' + patientName.givenName}`)).toBeTruthy(); await expect(quotation).toHaveText('Cancelled'); diff --git a/e2e/tests/testSenaiteIntegration.spec.ts b/e2e/tests/testSenaiteIntegration.spec.ts index 292369bf..0e9253d6 100644 --- a/e2e/tests/testSenaiteIntegration.spec.ts +++ b/e2e/tests/testSenaiteIntegration.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import { HomePage } from '../utils/functions/testBase'; import { patientName } from '../utils/functions/testBase'; +import { E2E_BASE_URL, E2E_SENAITE_URL } from '../utils/configs/globalSetup'; let homePage: HomePage; @@ -46,7 +47,7 @@ test('Editing patient details with a synced lab test order edits client details await expect(client).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.updatePatientDetails(); @@ -77,7 +78,7 @@ test('Editing a synced lab order edits corresponding analysis request in SENAITE await expect(analysisRequest).toHaveText('Blood urea nitrogen Template'); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.updateLabOrder(); @@ -111,7 +112,7 @@ test('Voiding a synced lab order cancels corresponding analysis request in SENAI await expect(analysisRequest).toHaveText('Blood urea nitrogen Template'); // replay - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await homePage.discontinueLabOrder(); @@ -140,7 +141,7 @@ test('Published coded lab results from SENAITE are viewable in O3', async ({ pag await expect(reviewState?.includes('Published')).toBeTruthy(); // verify - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.viewTestResults(); const testName = await page.locator('div:nth-child(2) >div> div.cds--data-table-container td:nth-child(1)').first(); await expect(testName).toContainText('Hepatitis C test - qualitative'); @@ -168,7 +169,7 @@ test('Published numeric lab results from SENAITE are viewable in O3', async ({ p await expect(reviewState?.includes('Published')).toBeTruthy(); // verify - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.viewTestResults(); const testName = await page.locator('div:nth-child(2) >div> div.cds--data-table-container td:nth-child(1)').first(); await expect(testName).toContainText('Total bilirubin'); @@ -196,7 +197,7 @@ test('Published free text lab results from SENAITE are viewable in O3', async ({ await expect(reviewState?.includes('Published')).toBeTruthy(); // verify - await page.goto(`${process.env.E2E_BASE_URL}/openmrs/spa/home`); + await page.goto(`${E2E_BASE_URL}`); await homePage.viewTestResults(); const testName = await page.locator('div:nth-child(2) >div> div.cds--data-table-container td:nth-child(1)').first(); await expect(testName).toHaveText('Stool microscopy with concentration'); diff --git a/e2e/utils/configs/globalSetup.ts b/e2e/utils/configs/globalSetup.ts index 5b43fbd0..f0fc091f 100644 --- a/e2e/utils/configs/globalSetup.ts +++ b/e2e/utils/configs/globalSetup.ts @@ -11,12 +11,18 @@ import { dotenv.config(); +export const E2E_BASE_URL = `${process.env.E2E_TEST_ENVIRONMENT}` == 'demo' ? `${process.env.E2E_BASE_URL_DEMO}` : `${process.env.E2E_TEST_ENVIRONMENT}` == 'qa' ? `${process.env.E2E_BASE_URL_QA}`: `${process.env.E2E_BASE_URL_DEV}`; +export const E2E_ODOO_URL = `${process.env.E2E_TEST_ENVIRONMENT}` == 'demo' ? `${process.env.E2E_ODOO_URL_DEMO}` : `${process.env.E2E_TEST_ENVIRONMENT}` == 'qa' ? `${process.env.E2E_ODOO_URL_QA}`: `${process.env.E2E_ODOO_URL_DEV}`; +export const E2E_SENAITE_URL = `${process.env.E2E_TEST_ENVIRONMENT}` == 'demo' ? `${process.env.E2E_SENAITE_URL_DEMO}` : `${process.env.E2E_TEST_ENVIRONMENT}` == 'qa' ? `${process.env.E2E_SENAITE_URL_QA}`: `${process.env.E2E_SENAITE_URL_DEV}`; +export const E2E_KEYCLOAK_URL = `${process.env.E2E_TEST_ENVIRONMENT}` == 'demo' ? `${process.env.E2E_KEYCLOAK_URL_DEMO}` : `${process.env.E2E_TEST_ENVIRONMENT}` == 'qa' ? `${process.env.E2E_KEYCLOAK_URL_QA}`: `${process.env.E2E_KEYCLOAK_URL_DEV}`; +export const E2E_ANALYTICS_URL = `${process.env.E2E_TEST_ENVIRONMENT}` == 'demo' ? `${process.env.E2E_ANALYTICS_URL_DEMO}` : `${process.env.E2E_TEST_ENVIRONMENT}` == 'qa' ? `${process.env.E2E_ANALYTICS_URL_QA}`: `${process.env.E2E_ANALYTICS_URL_DEV}`; + async function globalSetup() { const requestContext = await request.newContext(); const token = Buffer.from(`${process.env.E2E_USER_ADMIN_USERNAME}:${process.env.E2E_USER_ADMIN_PASSWORD}`).toString( 'base64', ); - await requestContext.post(`${process.env.E2E_BASE_URL}/ws/rest/v1/session`, { + await requestContext.post(`${process.env.E2E_BASE_URL_DEV}/ws/rest/v1/session`, { data: { sessionLocation: process.env.E2E_LOGIN_DEFAULT_LOCATION_UUID, locale: 'en', @@ -32,7 +38,7 @@ async function globalSetup() { export const api: WorkerFixture = async ({ playwright }, use) => { const ctx = await playwright.request.newContext({ - baseURL: `${process.env.E2E_BASE_URL}/ws/rest/v1/`, + baseURL: `${process.env.E2E_BASE_URL_DEV}/ws/rest/v1/`, httpCredentials: { username: process.env.E2E_USER_ADMIN_USERNAME ?? "", password: process.env.E2E_USER_ADMIN_PASSWORD ?? "", diff --git a/e2e/utils/functions/testBase.ts b/e2e/utils/functions/testBase.ts index a14332cb..6bfca02d 100644 --- a/e2e/utils/functions/testBase.ts +++ b/e2e/utils/functions/testBase.ts @@ -1,4 +1,12 @@ import { Page, expect } from '@playwright/test'; +import { + E2E_BASE_URL, + E2E_ODOO_URL, + E2E_SENAITE_URL, + E2E_KEYCLOAK_URL, + E2E_ANALYTICS_URL +} + from '../configs/globalSetup'; export var patientName = { firstName : '', @@ -27,7 +35,7 @@ export class HomePage { readonly patientSearchBar = () => this.page.locator('[data-testid="patientSearchBar"]'); async initiateLogin() { - await this.page.goto(`${process.env.E2E_BASE_URL}`); + await this.page.goto(`${E2E_BASE_URL}`); if (`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true') { await this.page.locator('#username').fill(`${process.env.E2E_USER_ADMIN_USERNAME}`); await this.page.getByRole('button', { name: 'Continue' }).click(); @@ -41,22 +49,24 @@ export class HomePage { await this.page.waitForTimeout(1000); await this.page.locator('button[type="submit"]').click(); } - await this.page.locator('label').filter({ hasText: 'Inpatient Ward' }).locator('span').first().click(); - await this.page.getByRole('button', { name: 'Confirm' }).click(); - - await expect(this.page.getByRole('button', { name: 'App Menu' })).toBeEnabled(); - await expect(this.page.getByRole('button', { name: 'Users' })).toBeEnabled(); - await expect(this.page.getByRole('button', { name: 'Add Patient' })).toBeEnabled(); - await expect(this.page.getByRole('button', { name: 'Implementer Tools' })).toBeEnabled(); - await expect(this.page.getByRole('button', { name: 'Search Patient' })).toBeEnabled(); + if (`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true' && !(await this.page.locator('#checkbox').isChecked()) + && await this.page.locator('input[role="searchbox"]').isVisible()) { + await this.page.locator('label').filter({ hasText: 'Inpatient Ward' }).locator('span').first().click(); + await this.page.getByRole('button', { name: 'Confirm' }).click(); + } else { + await this.page.locator('label').filter({ hasText: 'Inpatient Ward' }).locator('span').first().click(); + await this.page.getByRole('button', { name: 'Confirm' }).click(); + } + await delay(5000); + await this.expectAllButtonsToBePresent(); } async goToSuperset() { - await this.page.goto(`${process.env.E2E_ANALYTICS_URL}`); + await this.page.goto(`${E2E_ANALYTICS_URL}`); } async goToKeycloak() { - await this.page.goto(`${process.env.E2E_KEYCLOAK_URL}/admin/master/console`); + await this.page.goto(`${E2E_KEYCLOAK_URL}/admin/master/console`); await this.page.getByLabel('Username or email').fill('admin'); await this.page.getByLabel('Password').fill('password'); await this.page.getByRole('button', { name: 'Sign In' }).click(); @@ -64,7 +74,7 @@ export class HomePage { } async goToOdoo() { - await this.page.goto(`${process.env.E2E_ODOO_URL}`); + await this.page.goto(`${E2E_ODOO_URL}`); if (`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true') { await this.page.getByRole('link', { name: 'Login with Single Sign-On' }).click(); } else { @@ -78,7 +88,7 @@ export class HomePage { } async goToSENAITE() { - await this.page.goto(`${process.env.E2E_SENAITE_URL}`); + await this.page.goto(`${E2E_SENAITE_URL}`); if (!(`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true')) { await delay(3000); await this.page.locator('#__ac_name').fill(`${process.env.FOSS_E2E_USER_ADMIN_USERNAME}`); @@ -97,6 +107,10 @@ export class HomePage { } patientFullName = patientName.firstName + ' ' + patientName.givenName; + if (`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true') { + await this.makeDelayFor03ToLoad(); + } + await this.expectAllButtonsToBePresent(); await this.page.getByRole('button', { name: 'Add Patient' }).click(); await expect(this.page.getByRole('button', { name: 'Register Patient' })).toBeEnabled(); await this.page.getByLabel('First Name').clear(); @@ -120,7 +134,7 @@ export class HomePage { if (await this.page.getByTitle('close notification').isVisible()) { await this.page.getByTitle('close notification').click(); } - await this.page.getByRole('button', { name: 'Close' }).click(); + await this.page.getByRole('button', { name: 'Close', exact: true }).click(); await delay(3000); } @@ -130,15 +144,15 @@ export class HomePage { await this.page.getByRole('link', { name: `${patientFullName}` }).first().click(); } - async searchPatientId() { + async searchOpenMRSPatientID() { await this.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await expect(this.page.getByText('Actions')).toBeVisible(); + await expect(this.page.getByText('Actions', {exact: true})).toBeVisible(); await this.page.getByRole('button', { name: 'Actions', exact: true }).click(); await expect(this.page.getByText('Edit patient details')).toBeVisible(); await this.page.getByRole('menuitem', { name: 'Edit patient details' }).click(); await delay(4000); - await expect(this.page.getByText('Identifiers')).toBeVisible(); - await expect(this.page.getByText('OpenMRS ID')).toBeVisible(); + await expect(this.page.getByText('Identifiers', {exact: true})).toBeVisible(); + await expect(this.page.getByText('OpenMRS ID', {exact: true})).toBeVisible(); } async startPatientVisit() { @@ -161,7 +175,7 @@ export class HomePage { } async deletePatient() { - await this.page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/patients/index.htm`); + await this.page.goto(`${E2E_BASE_URL}/openmrs/admin/patients/index.htm`); await this.page.getByPlaceholder(' ').type(`${patientName.firstName + ' ' + patientName.givenName}`); await this.page.locator('#openmrsSearchTable tbody tr.odd td:nth-child(1)').click(); await this.page.locator('input[name="voidReason"]').fill('Delete patient created by smoke tests'); @@ -184,7 +198,7 @@ export class HomePage { await delay(2000); const patientCondition = await this.page.locator('table tbody tr:nth-child(1) td:nth-child(1)'); await expect(patientCondition).toHaveText('Typhoid fever'); - await this.page.getByRole('button', { name: 'Close' }).click(); + await this.page.getByRole('button', { name: 'Close', exact: true }).click(); } async addPatientBiometrics() { @@ -196,7 +210,7 @@ export class HomePage { await this.page.getByRole('button', { name: 'Save and close' }).click(); await expect(this.page.getByText('Biometrics saved')).toBeVisible(); - await this.page.getByRole('button', { name: 'Close' }).click(); + await this.page.getByRole('button', { name: 'Close', exact: true }).click(); } async addPatientAppointment() { @@ -241,7 +255,7 @@ export class HomePage { } async goToLabOrderForm() { - await this.page.locator('div').filter({ hasText: /^Form$/ }).getByRole('button').click(); + await this.page.getByLabel('Clinical forms').click(); await delay(3000); await expect(this.page.getByText('Laboratory Test Orders')).toBeVisible(); await this.page.getByText('Laboratory Test Orders').click(); @@ -250,14 +264,14 @@ export class HomePage { async saveLabOrder() { await this.page.getByRole('button', { name: 'Save and close' }).click(); await expect(this.page.getByText('Lab order(s) generated')).toBeVisible(); - await this.page.getByRole('button', { name: 'Close' }).click(); + await this.page.getByRole('button', { name: 'Close', exact: true }).click(); await delay(5000); } async updateLabOrder() { await this.page.getByRole('link', { name: 'Visits' }).click(); await this.page.getByRole('tab', { name: 'All encounters' }).click(); - await this.page.getByRole('row', { name: 'Encounter table actions menu' }).getByRole('button', { name: 'Encounter table actions menu' }).click(); + await this.page.getByRole('button', { name: 'Options', exact: true }).click(); await this.page.getByRole('menuitem', { name: 'Edit this encounter' }).click(); await this.page.locator('#tab select').selectOption('160225AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); await this.page.getByRole('button', { name: 'Save and close' }).click(); @@ -268,7 +282,7 @@ export class HomePage { async discontinueLabOrder() { await this.page.getByRole('link', { name: 'Visits' }).click(); await this.page.getByRole('tab', { name: 'All encounters' }).click(); - await this.page.getByRole('row', { name: 'Encounter table actions menu' }).getByRole('button', { name: 'Encounter table actions menu' }).click(); + await this.page.getByRole('button', { name: 'Options', exact: true }).click(); await this.page.getByRole('menuitem', { name: 'Delete this encounter' }).click(); await this.page.getByRole('button', { name: 'danger Delete' }).click(); @@ -308,18 +322,19 @@ export class HomePage { } async makeDrugOrder() { - await this.page.getByRole('complementary').filter({ hasText: 'Medications' }).getByRole('button').first().click(); + await this.page.getByLabel('Order basket', { exact: true }).click(); await delay(3000); await this.page.getByRole('button', { name: 'Add', exact: true }).nth(0).click(); + await delay(2000); await this.page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); await this.page.getByRole('button', { name: 'Order form' }).click(); await delay(4000); await this.page.getByPlaceholder('Dose').fill('4'); await this.page.getByRole('button', { name: 'Open', exact: true }).nth(1).click(); - await this.page.getByText('Intravenous').click(); + await this.page.getByText('Intravenous', {exact: true}).click(); await this.page.getByRole('button', { name: 'Open', exact: true }).nth(2).click(); - await this.page.getByText('Twice daily').click(); + await this.page.getByText('Twice daily', {exact: true}).click(); await this.page.getByPlaceholder('Additional dosing instructions (e.g. "Take after eating")').fill('Take after eating'); await this.page.getByLabel('Duration', { exact: true }).fill('5'); await this.page.getByLabel('Quantity to dispense').fill('12'); @@ -335,8 +350,8 @@ export class HomePage { } async editDrugOrder() { - await this.page.getByRole('button', { name: 'Actions menu' }).click(); - await this.page.getByRole('menuitem', { name: 'Modify' }).click(); + await this.page.getByRole('button', { name: 'Options', exact: true }).click(); + await this.page.getByRole('menuitem', { name: 'Modify', exact: true }).click(); await delay(4000); await this.page.getByPlaceholder('Dose').clear(); await this.page.getByPlaceholder('Dose').fill('8'); @@ -353,7 +368,7 @@ export class HomePage { } async discontinueDrugOrder() { - await this.page.getByRole('button', { name: 'Actions menu' }).click(); + await this.page.getByRole('button', { name: 'Options', exact: true }).click(); await this.page.getByRole('menuitem', { name: 'Discontinue' }).click(); await expect(this.page.getByText('Sign and close')).toBeVisible(); await this.page.getByRole('button', { name: 'Sign and close' }).focus(); @@ -392,7 +407,7 @@ export class HomePage { await this.page.getByRole('button', { name: 'Update Patient' }).click(); await expect(this.page.getByText('Patient Details Updated')).toBeVisible(); patientName.firstName = `${patientName.updatedFirstName}`; - await this.page.getByRole('button', { name: 'Close' }).click(); + await this.page.getByRole('button', { name: 'Close', exact: true }).click(); await delay(5000); } @@ -452,7 +467,7 @@ export class HomePage { } async deleteOpenMRSRole() { - await this.page.goto(`${process.env.E2E_BASE_URL}/openmrs/admin/users/role.list`); + await this.page.goto(`${E2E_BASE_URL}/openmrs/admin/users/role.list`); await this.unlinkInheritedOpenMRSRoles(); await this.page.getByRole('row', { name: `${randomOpenMRSRoleName.roleName}` }).getByRole('checkbox').check(); await this.page.getByRole('button', { name: 'Delete Selected Roles' }).click(); @@ -461,4 +476,26 @@ export class HomePage { await this.page.getByRole('link', { name: 'Log out' }).click(); } + async makeDelayFor03ToLoad() { + await this.page.goto(`${E2E_SENAITE_URL}`); + if (!(`${process.env.E2E_RUNNING_ON_OZONE_PRO}` == 'true')) { + await delay(3000); + await this.page.locator('#__ac_name').fill(`${process.env.FOSS_E2E_USER_ADMIN_USERNAME}`); + await delay(1000); + await this.page.locator('#__ac_password').fill('password'); + await delay(1000); + await this.page.locator('#buttons-login').click(); + } + await delay(4000); + await this.page.goto(`${E2E_BASE_URL}`); + } + + async expectAllButtonsToBePresent() { + await expect(this.page.getByRole('button', { name: 'Search Patient' })).toBeEnabled(); + await expect(this.page.getByRole('button', { name: 'Add Patient' })).toBeEnabled(); + await expect(this.page.getByRole('button', { name: 'Implementer Tools' })).toBeEnabled(); + await expect(this.page.getByRole('button', { name: 'Users' })).toBeEnabled(); + await expect(this.page.getByRole('button', { name: 'App Menu' })).toBeEnabled(); + } + } diff --git a/package.json b/package.json index 727f5346..76365ca7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.39.0", + "@playwright/test": "^1.40.0", "@types/node": "^20.8.10", "@types/react": "^18.2.22", "@types/react-dom": "^18.2.7", diff --git a/playwright.config.ts b/playwright.config.ts index 30b4e4ad..eca322c8 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,7 +2,6 @@ import { devices, PlaywrightTestConfig } from '@playwright/test'; import * as dotenv from 'dotenv'; dotenv.config(); -// See https://playwright.dev/docs/test-configuration. const config: PlaywrightTestConfig = { testDir: './e2e/tests', timeout: 3 * 60 * 1000, @@ -11,20 +10,22 @@ const config: PlaywrightTestConfig = { }, fullyParallel: true, forbidOnly: !!process.env.CI, + workers: process.env.CI ? 1 : 1, retries: 0, reporter: process.env.CI ? [['junit', { outputFile: 'results.xml' }], ['html']] : [['html']], globalSetup: require.resolve('./e2e/utils/configs/globalSetup'), use: { - baseURL: `${process.env.E2E_BASE_URL}/spa/`, + baseURL: `${process.env.E2E_BASE_URL_DEV}/spa/`, storageState: 'e2e/storageState.json', }, projects: [ { - name: 'webkit', + name: 'chromium', use: { - ...devices['Desktop Safari'], + ...devices['Desktop Chromium'], viewport: {width: 1920, height: 1080} }, + }, ], }; diff --git a/readme/choose_test_environment.png b/readme/choose_test_environment.png new file mode 100644 index 00000000..883a2193 Binary files /dev/null and b/readme/choose_test_environment.png differ