diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3222cd0..bd6078f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,27 +1,163 @@ -name: Publish to NPM repository +name: Ozone E2E Tests on: + schedule: + - cron: '0 0 * * 1,3,5' push: - branches: - - main - release: - types: - - created + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + inputs: + o3_url: + description: 'O3 URL' + required: false + odoo_url: + description: 'Odoo URL' + required: false + senaite_url: + description: 'SENAITE URL' + required: false + superset_url: + description: 'Superset URL' + required: false + test_pro: + description: 'Running Ozone on Pro?' + required: false + default: 'true' + type: choice + options: + - true + - false + environment: + description: 'Choose test environment' + required: false + default: 'dev' + type: choice + options: + - dev + - qa + - demo + test_profile: + description: 'Choose test profile' + required: false + default: 'e2e-tests-pro' + type: choice + options: + - e2e-tests-pro + - e2e-tests-foss + - e2e-tests-openmrs-his jobs: - publish: + build: runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - name: Checkout repository + uses: actions/checkout@v4 + with: + repository: ${{ github.repository }} + + - name: Setup node + uses: actions/setup-node@v4 with: node-version: 20 - registry-url: 'https://nexus.mekomsolutions.net/repository/npm-public/' + + - name: Cache dependencies + id: cache + uses: actions/cache@v4 + with: + path: '**/node_modules' + key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }} - name: Install dependencies run: yarn install - - name: Publish to NPM repository - run: yarn publish + - name: Install Playwright browsers + run: npx playwright install chromium + + - name: Run E2E tests + id: jobStatusPretty env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + O3_URL_DEV: '${{ github.event.inputs.o3_url }}' + ODOO_URL_DEV: '${{ github.event.inputs.odoo_url }}' + SENAITE_URL_DEV: '${{ github.event.inputs.senaite_url }}' + SUPERSET_URL_DEV: '${{ github.event.inputs.superset_url }}' + TEST_PRO: '${{ github.event.inputs.test_pro }}' + TEST_ENVIRONMENT: '${{ github.event.inputs.environment }}' + TEST_PROFILE: '${{ github.event.inputs.test_profile }}' + KEYCLOAK_USERNAME: '${{ secrets.KEYCLOAK_USERNAME }}' + KEYCLOAK_PASSWORD: '${{ secrets.KEYCLOAK_PASSWORD }}' + + run: | + npm run ${{ github.event.inputs.test_profile }} + + if: ${{ github.event_name != 'pull_request' }} + if [[ ${{ job.status }} == "success" ]]; then + jobStatusPretty="✅ Passing" + else + jobStatusPretty="❌ Failing" + fi + 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: + payload: | + { + "blocks": [ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "GitHub Actions report from " + }, + { + "type": "link", + "url": "https://github.com/ozone-his/ozone-e2e", + "text": "${{ github.repository }}", + "style": { + "bold": false + } + }, + { + "type": "text", + "text": ":\n" + } + ] + }, + { + "type": "rich_text_quote", + "elements": [ + { + "type": "link", + "url": "https://ozone-dev.mekomsolutions.net", + "text": "Ozone Dev" + }, + { + "type": "text", + "text": " server QA status: ${{ env.jobStatusPretty }}" + } + ] + } + ] + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + + - name: Upload report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 00000000..89cbab7e --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,93 @@ +name: E2E Test Profile + +on: + workflow_dispatch: + + inputs: + o3_url: + description: 'O3 URL' + required: false + odoo_url: + description: 'Odoo URL' + required: false + senaite_url: + description: 'SENAITE URL' + required: false + superset_url: + description: 'Superset URL' + required: false + test_pro: + description: 'Running Ozone on Pro?' + required: false + default: 'true' + type: choice + options: + - true + - false + environment: + description: 'Choose test environment' + required: false + default: 'dev' + type: choice + options: + - dev + - qa + - demo + test_profile: + description: 'Choose test profile' + required: false + default: 'e2e-tests-pro' + type: choice + options: + - e2e-tests-pro + - e2e-tests-foss + - e2e-tests-openmrs-his +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + repository: ${{ github.repository }} + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Cache dependencies + id: cache + uses: actions/cache@v4 + 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 + + - name: Run E2E tests + id: jobStatusPretty + env: + O3_URL_DEV: '${{ github.event.inputs.o3_url }}' + ODOO_URL_DEV: '${{ github.event.inputs.odoo_url }}' + SENAITE_URL_DEV: '${{ github.event.inputs.senaite_url }}' + SUPERSET_URL_DEV: '${{ github.event.inputs.superset_url }}' + TEST_PRO: '${{ github.event.inputs.test_pro }}' + TEST_ENVIRONMENT: '${{ github.event.inputs.environment }}' + TEST_PROFILE: '${{ github.event.inputs.test_profile }}' + + run: | + npm run $TEST_PROFILE + + - name: Upload report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.github/workflows/foss.yml b/.github/workflows/foss.yml deleted file mode 100644 index 4e6bb2e2..00000000 --- a/.github/workflows/foss.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Ozone FOSS E2E Tests - -on: - - workflow_dispatch: - inputs: - e2e_base_url_input: - description: 'O3 URL' - required: true - e2e_odoo_url_input: - description: 'Odoo URL' - required: true - e2e_senaite_url_input: - description: 'SENAITE URL' - required: true -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - repository: ${{ github.repository }} - - - name: Setup node - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Cache dependencies - id: cache - uses: actions/cache@v4 - 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: - O3_DEV: '${{ github.event.inputs.e2e_base_url_input }}' - ODOO_URL_DEV: '${{ github.event.inputs.e2e_odoo_url_input }}' - SENAITE_URL_DEV: '${{ github.event.inputs.e2e_senaite_url_input }}' - RUNNING_OZONE_ON_PRO: 'false' - run: npm run e2e-tests-foss - - - name: Upload report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/e2e/tests/erpnext-openmrs-flows.spec.ts b/e2e/tests/erpnext-openmrs-flows.spec.ts index 696c77ca..39f0a8dd 100644 --- a/e2e/tests/erpnext-openmrs-flows.spec.ts +++ b/e2e/tests/erpnext-openmrs-flows.spec.ts @@ -17,8 +17,8 @@ test.beforeEach(async ({ page }) => { test('Ordering a lab test for an OpenMRS patient creates the corresponding ERPNext customer.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); // verify @@ -31,8 +31,8 @@ test('Ordering a lab test for an OpenMRS patient creates the corresponding ERPNe test('Ordering a drug for an OpenMRS patient creates the corresponding ERPNext customer with a filled quotation.', async ({ page }) => { // replay - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); @@ -50,8 +50,8 @@ test('Ordering a drug for an OpenMRS patient creates the corresponding ERPNext c test('Editing the details of an OpenMRS patient with a synced lab order edits the corresponding ERPNext customer details.', async ({ page }) => { // setup - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await erpnext.open(); await erpnext.searchCustomer(); @@ -72,8 +72,8 @@ test('Editing the details of an OpenMRS patient with a synced lab order edits th test('Editing the details of an OpenMRS patient with a synced drug order edits the corresponding ERPNext customer details.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await erpnext.open(); @@ -95,8 +95,8 @@ test('Editing the details of an OpenMRS patient with a synced drug order edits t test('Ending an OpenMRS patient visit with a synced drug order updates the corresponding ERPNext draft quotation to an open state.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await erpnext.open(); @@ -117,10 +117,10 @@ test('Ending an OpenMRS patient visit with a synced drug order updates the corre await erpnext.deleteQuotation(); }); -test('Revising a synced OpenMRS drug order edits the corresponding ERPNext quotation item.', async ({ page }) => { +test('Revising details of a synced OpenMRS drug order modifies the corresponding ERPNext quotation item.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await erpnext.open(); @@ -131,7 +131,7 @@ test('Revising a synced OpenMRS drug order edits the corresponding ERPNext quota // replay await page.goto(`${O3_URL}`); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.editDrugOrder(); + await openmrs.modifyDrugOrderDescription(); // verify await page.goto(`${ERPNEXT_URL}/app/home`); @@ -162,8 +162,8 @@ test('Ordering a drug with a free text medication dosage for an OpenMRS patient test('Discontinuing a synced OpenMRS drug order for an ERPNext customer with a single quotation line removes the corresponding quotation.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await erpnext.open(); @@ -185,8 +185,8 @@ test('Discontinuing a synced OpenMRS drug order for an ERPNext customer with a s test('Ordering a drug for an OpenMRS patient within a visit creates the corresponding ERPNext customer with a filled quotation linked to the visit.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await erpnext.open(); @@ -198,8 +198,8 @@ test('Ordering a drug for an OpenMRS patient within a visit creates the correspo await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); await openmrs.endPatientVisit(); await openmrs.startPatientVisit(); - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 81mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 81mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); diff --git a/e2e/tests/odoo-openmrs-flows.spec.ts b/e2e/tests/odoo-openmrs-flows.spec.ts index f46b7aa6..826f343c 100644 --- a/e2e/tests/odoo-openmrs-flows.spec.ts +++ b/e2e/tests/odoo-openmrs-flows.spec.ts @@ -17,8 +17,8 @@ test.beforeEach(async ({ page }) => { test('Ordering a lab test for an OpenMRS patient creates the corresponding Odoo customer with a filled quotation.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); // verify @@ -27,12 +27,13 @@ test('Ordering a lab test for an OpenMRS patient creates the corresponding Odoo await odoo.searchCustomer(); await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(4)')).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`); await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(8) span')).toHaveText('Quotation'); + await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(7) span')).toHaveText('$ 27.50'); }); test('Editing the details of an OpenMRS patient with a synced lab order edits the corresponding Odoo customer details.', async ({ page }) => { // setup - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await odoo.open(); await odoo.navigateToSales(); @@ -55,8 +56,8 @@ test('Editing the details of an OpenMRS patient with a synced lab order edits th test('Ordering a drug for an OpenMRS patient creates the corresponding Odoo customer with a filled quotation.', async ({ page }) => { // replay - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); @@ -66,12 +67,13 @@ test('Ordering a drug for an OpenMRS patient creates the corresponding Odoo cust await odoo.searchCustomer(); await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(4)')).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`); await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(8) span')).toHaveText('Quotation'); + await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(7) span')).toHaveText('$ 14.88'); }); test('Editing the details of an OpenMRS patient with a synced drug order edits the corresponding Odoo customer details.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await odoo.open(); @@ -93,10 +95,10 @@ test('Editing the details of an OpenMRS patient with a synced drug order edits t await expect(page.locator('tr.o_data_row:nth-child(1) td:nth-child(8) span')).toHaveText('Quotation'); }); -test('Revising a synced OpenMRS drug order edits the corresponding Odoo quotation line.', async ({ page }) => { +test('Revising details of a synced OpenMRS drug order modifies the corresponding Odoo quotation line.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await odoo.open(); @@ -107,11 +109,12 @@ test('Revising a synced OpenMRS drug order edits the corresponding Odoo quotatio const drugOrderItem = await page.locator('table tbody td.o_data_cell:nth-child(3) span'); await expect(drugOrderItem).toContainText('4.0 Tablet'); await expect(drugOrderItem).toContainText('Twice daily - 5 day'); + await expect(page.locator('td.o_data_cell:nth-child(9) span')).toHaveText('$ 14.88'); // replay await page.goto(`${O3_URL}`); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.editDrugOrder(); + await openmrs.modifyDrugOrderDescription(); // verify await page.goto(`${ODOO_URL}`); @@ -120,12 +123,13 @@ test('Revising a synced OpenMRS drug order edits the corresponding Odoo quotatio await page.getByRole('cell', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click(); await expect(drugOrderItem).toContainText('8.0 Tablet'); await expect(drugOrderItem).toContainText('Thrice daily - 6 day'); + await expect(page.locator('td.o_data_cell:nth-child(9) span')).toHaveText('$ 9.92'); }); test('Discontinuing a synced OpenMRS drug order for an Odoo customer with a single quotation line removes the corresponding quotation.', async ({ page }) => { // setup - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await odoo.open(); @@ -153,12 +157,12 @@ test('Discontinuing a synced OpenMRS drug order for an Odoo customer with a sing test('Discontinuing a synced OpenMRS drug order for an Odoo customer with multiple quotation lines removes the corresponding quoatation.', async ({ page }) => { // setup - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.goToDrugOrderForm(); - await page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await openmrs.navigateToDrugOrderForm(); + await page.getByRole('searchbox').fill('Aspirin 325mg'); await openmrs.fillDrugOrderForm(); await openmrs.saveDrugOrder(); await odoo.open(); @@ -198,8 +202,8 @@ test('Ordering a drug with a free text medication dosage for an OpenMRS patient test('Discontinuing a synced OpenMRS lab order for an Odoo customer with a single quotation line cancels the corresponding quotation.', async ({ page }) => { // setup - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await odoo.open(); await odoo.navigateToSales(); diff --git a/e2e/tests/odoo-superset-flows.spec.ts b/e2e/tests/odoo-superset-flows.spec.ts index a0c8e428..3622e582 100644 --- a/e2e/tests/odoo-superset-flows.spec.ts +++ b/e2e/tests/odoo-superset-flows.spec.ts @@ -21,8 +21,8 @@ test.beforeEach(async ({ page }) => { test(`Creating an Odoo sale order line generates an entry in Superset's sale_order_lines table.`, async ({ page }) => { // setup await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Complete blood count'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Complete blood count'); await openmrs.saveLabOrder(); await superset.open(); await superset.selectDBSchema(); @@ -75,8 +75,8 @@ test(`A (synced) sale order line in Odoo generates an entry in Superset's sale_o await superset.clearSQLEditor(); await page.goto(`${O3_URL}`); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Haemoglobin'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Haemoglobin'); await openmrs.saveLabOrder(); // replay diff --git a/e2e/tests/openmrs-senaite-flows.spec.ts b/e2e/tests/openmrs-senaite-flows.spec.ts index c18e3833..91cda879 100644 --- a/e2e/tests/openmrs-senaite-flows.spec.ts +++ b/e2e/tests/openmrs-senaite-flows.spec.ts @@ -17,8 +17,8 @@ test.beforeEach(async ({ page }) => { test('Ordering a lab test for an OpenMRS patient creates the corresponding SENAITE client with an analysis request.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); // verify @@ -29,8 +29,8 @@ test('Ordering a lab test for an OpenMRS patient creates the corresponding SENAI test('Editing the details of an OpenMRS patient with a synced lab order edits the corresponding SENAITE client details.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await senaite.open(); await senaite.searchClient(); @@ -47,8 +47,8 @@ test('Editing the details of an OpenMRS patient with a synced lab order edits th test('Voiding a synced OpenMRS lab order cancels the corresponding SENAITE analysis request.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await senaite.open(); await senaite.searchClient(); @@ -71,8 +71,8 @@ test('Voiding a synced OpenMRS lab order cancels the corresponding SENAITE analy test('Published coded lab results from SENAITE are viewable in the OpenMRS lab results viewer.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Hepatitis C test - qualitative'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Hepatitis C test - qualitative'); await openmrs.saveLabOrder(); await senaite.open(); await senaite.searchClient(); @@ -89,8 +89,8 @@ test('Published coded lab results from SENAITE are viewable in the OpenMRS lab r test('Published numeric lab results from SENAITE are viewable in the OpenMRS lab results viewer.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Total bilirubin'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Total bilirubin'); await openmrs.saveLabOrder(); await senaite.open(); await senaite.searchClient(); @@ -107,8 +107,8 @@ test('Published numeric lab results from SENAITE are viewable in the OpenMRS lab test('Published free text lab results from SENAITE are viewable in the OpenMRS lab results viewer.', async ({ page }) => { // replay - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Stool microscopy with concentration'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Stool microscopy with concentration'); await openmrs.saveLabOrder(); await senaite.open(); await senaite.searchClient(); diff --git a/e2e/tests/openmrs-superset-flows.spec.ts b/e2e/tests/openmrs-superset-flows.spec.ts index 0d36b487..bf008238 100644 --- a/e2e/tests/openmrs-superset-flows.spec.ts +++ b/e2e/tests/openmrs-superset-flows.spec.ts @@ -109,8 +109,8 @@ test(`Creating an OpenMRS order creates the order in Superset's orders table.`, // replay await page.goto(`${O3_URL}`); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); // verify @@ -157,8 +157,8 @@ test(`Creating an OpenMRS encounter creates the encounter in Superset's encounte // replay await page.goto(`${O3_URL}`); await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`); - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); // verify @@ -424,8 +424,8 @@ test(`Voiding an OpenMRS encounter updates the encounter in Superset's encounter await openmrs.searchPatientId(); const patientIdentifier = await page.locator('#demographics section p:nth-child(2)').textContent(); await openmrs.startPatientVisit(); - await openmrs.goToLabOrderForm(); - await page.getByPlaceholder('Search for a test type').fill('Blood urea nitrogen'); + await openmrs.navigateToLabOrderForm(); + await page.getByRole('searchbox').fill('Blood urea nitrogen'); await openmrs.saveLabOrder(); await superset.open(); await superset.selectDBSchema(); diff --git a/e2e/utils/functions/erpnext.ts b/e2e/utils/functions/erpnext.ts index a41e0520..53952e36 100644 --- a/e2e/utils/functions/erpnext.ts +++ b/e2e/utils/functions/erpnext.ts @@ -18,7 +18,7 @@ export class ERPNext { await this.page.getByRole('link', { name: 'Customer', exact: true }).click(); await this.page.getByPlaceholder('Customer Name').clear(); await this.page.getByPlaceholder('Customer Name').fill(`${patientName.givenName}`); - await delay(3000); + await delay(5000); } async searchQuotation() { @@ -27,7 +27,7 @@ export class ERPNext { await this.page.getByPlaceholder(/title/i).clear(); await this.page.getByPlaceholder(/party/i).clear(); await this.page.getByPlaceholder(/title/i).fill(`${patientName.givenName}`); - await delay(3000); + await delay(5000); } async deleteQuotation() { diff --git a/e2e/utils/functions/odoo.ts b/e2e/utils/functions/odoo.ts index 557fcb97..4c11ef29 100644 --- a/e2e/utils/functions/odoo.ts +++ b/e2e/utils/functions/odoo.ts @@ -1,7 +1,6 @@ import { expect, Page } from '@playwright/test'; -import { patientName } from '../functions/openmrs'; import { ODOO_URL } from '../configs/globalSetup'; -import { delay } from './openmrs'; +import { delay, patientName } from './openmrs'; export class Odoo { constructor(readonly page: Page) {} @@ -9,33 +8,27 @@ export class Odoo { async open() { await this.page.goto(`${ODOO_URL}`); if (`${process.env.TEST_PRO}` == 'true') { - await this.page.getByRole('link', { name: 'Login with Single Sign-On' }).click(); + await this.page.locator('div.o_login_auth div a').click(); } else { await this.page.locator('#login').fill(`${process.env.ODOO_USERNAME_ON_FOSS}`); - await delay(1000); await this.page.locator('#password').fill(`${process.env.ODOO_PASSWORD_ON_FOSS}`); - await delay(1000); await this.page.locator('button[type="submit"]').click(); } await expect(this.page).toHaveURL(/.*web/); } - async createCustomer() { - await this.page.getByPlaceholder('Search...').type(`${patientName.firstName + ' ' + patientName.givenName}`); - await this.page.getByPlaceholder('Search...').press('Enter'); - await delay(2000); - } - async searchCustomer() { - await this.page.getByPlaceholder('Search...').type(`${patientName.firstName + ' ' + patientName.givenName}`); - await this.page.getByPlaceholder('Search...').press('Enter'); + await expect(this.page.locator('.o_searchview_input')).toBeVisible(); + await this.page.locator('.o_searchview_input').fill(`${patientName.firstName + ' ' + patientName.givenName}`); + await this.page.locator('.o_searchview_input').press('Enter'); await delay(2000); } async navigateToSales() { await this.page.locator("//a[contains(@class, 'full')]").click(); - await this.page.getByRole('menuitem', { name: 'Sales' }).click(); - await expect(this.page.locator('.breadcrumb-item')).toHaveText('Quotations'); + await expect(this.page.getByRole('menuitem', { name: /sales/i })).toBeVisible(); + await this.page.getByRole('menuitem', { name: /sales/i }).click(); + await expect(this.page.locator('.breadcrumb-item')).toHaveText(/quotations/i); } async createSaleOrderLine() { diff --git a/e2e/utils/functions/openmrs.ts b/e2e/utils/functions/openmrs.ts index 5ea56cc6..abe68875 100644 --- a/e2e/utils/functions/openmrs.ts +++ b/e2e/utils/functions/openmrs.ts @@ -136,7 +136,7 @@ export class OpenMRS { await this.page.locator('label').filter({ hasText: 'Facility Visit' }).locator('span').first().click(); await this.page.locator('form').getByRole('button', { name: 'Start visit' }).click(); await expect(this.page.getByText('Facility Visit started successfully')).toBeVisible(); - await delay(4000); + await delay(5000); } async endPatientVisit() { @@ -150,13 +150,12 @@ export class OpenMRS { async voidPatient() { await this.page.goto(`${O3_URL}/openmrs/admin/patients/index.htm`); - await expect(await this.page.getByPlaceholder(' ')).toBeVisible(); + await expect(this.page.getByPlaceholder(' ')).toBeVisible(); 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('Void patient created by smoke test'); await this.page.getByRole('button', { name: 'Delete Patient', exact: true }).click(); - const message = await this.page.locator('//*[@id="patientFormVoided"]').textContent(); - await expect(message?.includes('This patient has been deleted')).toBeTruthy(); + await expect(this.page.locator('//*[@id="patientFormVoided"]')).toContainText('This patient has been deleted'); } async addPatientCondition() { @@ -219,8 +218,11 @@ export class OpenMRS { await expect(this.page.getByText('Appointment cancelled successfully')).toBeVisible(); } - async goToLabOrderForm() { - await this.page.getByLabel('Order basket', { exact: true }).click(); + async navigateToLabOrderForm() { + await expect(this.page.getByLabel('Order basket')).toBeVisible(); + await this.page.getByLabel('Order basket').click(); + await delay(2000); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(1)).toBeVisible(); await this.page.getByRole('button', { name: 'Add', exact: true }).nth(1).click(); } @@ -256,14 +258,16 @@ export class OpenMRS { await this.page.getByRole('link', { name: 'Results Viewer' }).click(); } - async goToDrugOrderForm() { - await this.page.getByLabel('Order basket', { exact: true }).click(); + async navigateToDrugOrderForm() { + await expect(this.page.getByLabel('Order basket')).toBeVisible(); + await this.page.getByLabel('Order basket').click(); + await delay(2000); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(0)).toBeVisible(); await this.page.getByRole('button', { name: 'Add', exact: true }).nth(0).click(); } async fillDrugOrderForm() { await this.page.getByRole('button', { name: 'Order form' }).click(); - await delay(2000); await this.page.getByPlaceholder('Dose').fill('4'); await this.page.getByRole('button', { name: 'Open', exact: true }).nth(1).click(); await this.page.getByText('Intravenous', {exact: true}).click(); @@ -273,7 +277,7 @@ export class OpenMRS { await this.page.getByLabel('Duration', { exact: true }).fill('5'); await this.page.getByLabel('Quantity to dispense').fill('12'); await this.page.getByLabel('Prescription refills').fill('3'); - await this.page.getByPlaceholder('e.g. "Hypertension"').type('Hypertension'); + await this.page.locator('#indication').fill('Hypertension'); } async saveDrugOrder() { @@ -287,17 +291,19 @@ export class OpenMRS { } async createDrugOrderWithFreeTextDosage() { - await this.page.getByLabel('Order basket', { exact: true }).click(); + await expect(this.page.getByLabel('Order basket')).toBeVisible(); + await this.page.getByLabel('Order basket').click(); + await delay(2000); + await expect(this.page.getByRole('button', { name: 'Add', exact: true }).nth(0)).toBeVisible(); await this.page.getByRole('button', { name: 'Add', exact: true }).nth(0).click(); - await this.page.getByPlaceholder('Search for a drug or orderset (e.g. "Aspirin")').fill('Aspirin 325mg'); + await this.page.getByRole('searchbox').fill('Aspirin 325mg'); await this.page.getByRole('button', { name: 'Order form' }).click(); - await delay(2000); await this.page.locator('div').filter({ hasText: /^Off$/ }).locator('div').click(); await this.page.getByPlaceholder('Free text dosage').fill('Take up to three tablets per day'); await this.page.getByLabel('Duration', { exact: true }).fill('3'); await this.page.getByLabel('Quantity to dispense').fill('9'); await this.page.getByLabel('Prescription refills').fill('2'); - await this.page.getByPlaceholder('e.g. "Hypertension"').type('Hypertension'); + await this.page.locator('#indication').fill('Hypertension'); await this.page.getByRole('button', { name: 'Save order' }).focus(); await expect(this.page.getByText('Save order')).toBeVisible(); await this.page.getByRole('button', { name: 'Save order' }).click(); @@ -307,7 +313,7 @@ export class OpenMRS { await delay(5000); } - async editDrugOrder() { + async modifyDrugOrderDescription() { await this.page.getByRole('button', { name: 'Options', exact: true }).click(); await this.page.getByRole('menuitem', { name: 'Modify', exact: true }).click(); await this.page.getByPlaceholder('Dose').clear(); @@ -340,7 +346,6 @@ export class OpenMRS { await this.page.getByRole('button', { name: 'Actions', exact: true }).click(); await this.page.getByRole('menuitem', { name: 'Edit patient details' }).click(); await delay(4000); - await this.page.getByLabel('First Name').click(); await this.page.getByLabel('First Name').clear(); await this.page.getByLabel('First Name').type(`${patientName.updatedFirstName}`); await delay(4000); diff --git a/package.json b/package.json index 28afc93a..319d803b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ ], "scripts": { "e2e-tests-pro": "npx playwright test", - "e2e-tests-foss": "npx playwright test odoo-openmrs erpnext-openmrs openmrs-senaite" + "e2e-tests-foss": "npx playwright test odoo-openmrs erpnext-openmrs openmrs-senaite", + "e2e-tests-openmrs-his": "npx playwright test odoo-openmrs openmrs-senaite" }, "publishConfig": { "registry": "https://nexus.mekomsolutions.net/repository/npm-public/"