Skip to content

Commit

Permalink
OZ-541: E2E tests for the flows between O3 and ERPNext. (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdaud authored May 20, 2024
1 parent 25b6352 commit f2a626b
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 18 deletions.
9 changes: 9 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ O3_URL_DEV=https://ozone-dev.mekomsolutions.net
O3_URL_QA=https://ozone-qa.mekomsolutions.net
O3_URL_DEMO=https://demo.ozone-his.com

# ERPNEXT
ERPNEXT_URL_DEV=https://erpnext.ozone-dev.mekomsolutions.net
ERPNEXT_URL_QA=
ERPNEXT_URL_DEMO=

# Odoo
ODOO_URL_DEV=https://erp.ozone-dev.mekomsolutions.net
ODOO_URL_QA=https://erp.ozone-qa.mekomsolutions.net
Expand Down Expand Up @@ -47,6 +52,10 @@ KEYCLOAK_URL_DEMO=https://auth.demo.ozone-his.com
O3_USERNAME_ON_FOSS=admin
O3_PASSWORD_ON_FOSS=Admin123

# ERPNext test user credentials
ERPNEXT_USERNAME=Administrator
ERPNEXT_PASSWORD=password

# Odoo test user credentials for FOSS
ODOO_USERNAME_ON_FOSS=admin
ODOO_PASSWORD_ON_FOSS=admin
Expand Down
142 changes: 142 additions & 0 deletions e2e/tests/erpnext-openmrs-flows.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { test, expect } from '@playwright/test';
import { ERPNext } from '../utils/functions/erpnext';
import { O3_URL, ERPNEXT_URL } from '../utils/configs/globalSetup';
import { OpenMRS, patientName } from '../utils/functions/openmrs';

let openmrs: OpenMRS;
let erpnext: ERPNext;

test.beforeEach(async ({ page }) => {
openmrs = new OpenMRS(page);
erpnext = new ERPNext(page);

await openmrs.login();
await expect(page).toHaveURL(/.*home/);
await openmrs.createPatient();
await openmrs.startPatientVisit();
});

test('Ordering a lab test for an OpenMRS patient creates the corresponding ERPNext customer.', async ({ page }) => {
// replay
await openmrs.createLabOrder();

// verify
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchCustomer();
const customer = await page.locator(".bold a:nth-child(1)");
await expect(customer).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`);
await openmrs.deletePatient();
});

test('Ordering a drug for an OpenMRS patient creates the corresponding ERPNext customer with a filled quotation.', async ({ page }) => {
// replay
await openmrs.createDrugOrder();

// verify
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchCustomer();
const customer = await page.locator(".bold a:nth-child(1)");
await expect(customer).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`);
await page.getByRole('link', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click();
await page.locator('#customer-dashboard_tab-tab').click();
await page.getByLabel('Dashboard').getByText('Quotation').click();
await erpnext.searchQuotation();
await expect(page.getByText('Draft').nth(0)).toBeVisible();
await openmrs.deletePatient();
await erpnext.deleteQuotation();
});

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.createDrugOrder();
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchQuotation();

const quotationStatus = await page.locator('div.level-left.ellipsis div:nth-child(3) span span');
await expect(quotationStatus).toHaveText('Draft');

// replay
await page.goto(`${O3_URL}`);
await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`);
await openmrs.endPatientVisit();

// verify
await page.goto(`${ERPNEXT_URL}/app/home`);
await erpnext.searchQuotation();
await expect(quotationStatus).toHaveText('Open');
await erpnext.voidQuotation();
await openmrs.deletePatient();
await erpnext.deleteQuotation();
});

test('Revising a synced OpenMRS drug order edits the corresponding ERPNext quotation item.', async ({ page }) => {
// setup
await openmrs.createDrugOrder();
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchQuotation();
await page.getByRole('link', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click();
const quantity = await page.locator("div.bold:nth-child(4) div:nth-child(2) div");
await expect(quantity).toHaveText('12');

// replay
await page.goto(`${O3_URL}`);
await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`);
await openmrs.editDrugOrder();

// verify
await page.goto(`${ERPNEXT_URL}/app/home`);
await erpnext.searchQuotation();
await page.getByRole('link', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click();
await expect(quantity).toHaveText('8');
await openmrs.deletePatient();
await erpnext.deleteQuotation();
});

test('Ordering a drug with a free text medication dosage for an OpenMRS patient creates the corresponding ERPNext customer with a filled quotation.', async ({ page }) => {
// replay
await openmrs.createDrugOrderWithFreeTextDosage();

// verify
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchCustomer();
const customer = await page.locator(".bold a:nth-child(1)");
await expect(customer).toContainText(`${patientName.firstName + ' ' + patientName.givenName}`);
await page.getByRole('link', { name: `${patientName.firstName + ' ' + patientName.givenName}` }).click();
await page.locator('#customer-dashboard_tab-tab').click();
await page.getByLabel('Dashboard').getByText('Quotation').click();
await erpnext.searchQuotation();
await expect(page.getByText('Draft').nth(0)).toBeVisible();
await openmrs.deletePatient();
await erpnext.deleteQuotation();
});

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.createDrugOrder();
await erpnext.open();
await expect(page).toHaveURL(/.*home/);
await erpnext.searchQuotation();
await expect(page.getByText(`${patientName.firstName + ' ' + patientName.givenName}`)).toBeVisible();

// replay
await page.goto(`${O3_URL}`);
await openmrs.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`);
await openmrs.discontinueDrugOrder();

// verify
await page.goto(`${ERPNEXT_URL}/app/home`);
await erpnext.searchQuotation();
await expect(page.getByText(`${patientName.firstName + ' ' + patientName.givenName}`)).not.toBeVisible();
await expect(page.getByText('No Quotation found')).toBeVisible();
await openmrs.deletePatient();
});

test.afterEach(async ({ page }) => {
await erpnext.deleteCustomer();
await page.close();
});
1 change: 0 additions & 1 deletion e2e/tests/odoo-openmrs-flows.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ test('Discontinuing a synced OpenMRS drug order for an Odoo customer with a sing
await expect(QuotationItem).not.toHaveText('Aspirin 325mg');
});


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();
Expand Down
1 change: 1 addition & 0 deletions e2e/utils/configs/globalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
dotenv.config();

export const O3_URL = `${process.env.TEST_ENVIRONMENT}` == 'demo' ? `${process.env.O3_URL_DEMO}` : `${process.env.TEST_ENVIRONMENT}` == 'qa' ? `${process.env.O3_URL_QA}`: `${process.env.O3_URL_DEV}`;
export const ERPNEXT_URL = `${process.env.TEST_ENVIRONMENT}` == 'demo' ? `${process.env.ERPNEXT_URL_DEMO}` : `${process.env.TEST_ENVIRONMENT}` == 'qa' ? `${process.env.ERPNEXT_URL_QA}`: `${process.env.ERPNEXT_URL_DEV}`;
export const ODOO_URL = `${process.env.TEST_ENVIRONMENT}` == 'demo' ? `${process.env.ODOO_URL_DEMO}` : `${process.env.TEST_ENVIRONMENT}` == 'qa' ? `${process.env.ODOO_URL_QA}`: `${process.env.ODOO_URL_DEV}`;
export const SENAITE_URL = `${process.env.TEST_ENVIRONMENT}` == 'demo' ? `${process.env.SENAITE_URL_DEMO}` : `${process.env.TEST_ENVIRONMENT}` == 'qa' ? `${process.env.SENAITE_URL_QA}`: `${process.env.SENAITE_URL_DEV}`;
export const KEYCLOAK_URL = `${process.env.TEST_ENVIRONMENT}` == 'demo' ? `${process.env.KEYCLOAK_URL_DEMO}` : `${process.env.TEST_ENVIRONMENT}` == 'qa' ? `${process.env.KEYCLOAK_URL_QA}`: `${process.env.KEYCLOAK_URL_DEV}`;
Expand Down
63 changes: 63 additions & 0 deletions e2e/utils/functions/erpnext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Page, expect } from '@playwright/test';
import { delay, patientName } from './openmrs';
import { ERPNEXT_URL } from '../configs/globalSetup';

export class ERPNext {
constructor(readonly page: Page) {}

async open() {
await this.page.goto(`${ERPNEXT_URL}`);
await this.page.locator('input#login_email').fill(`${process.env.ERPNEXT_USERNAME}`);
await this.page.locator('input#login_password').fill(`${process.env.ERPNEXT_PASSWORD}`);
await this.page.locator('button.btn-login').click();
}

async searchCustomer() {
await this.page.getByRole('link', { name: /selling/i }).click();
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);
}

async searchQuotation() {
await this.page.getByRole('link', { name: /selling/i }).click();
await this.page.getByRole('link', { name: 'Quotation', exact: true }).click();
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);
}

async deleteQuotation() {
await this.page.goto(`${ERPNEXT_URL}/app/quotation`);
await this.searchQuotation();
await this.page.getByRole('checkbox', { name: 'Select All' }).check();
await delay(2500);
await this.page.getByRole('button', { name: 'Actions' }).click();
await this.page.getByRole('link', { name: 'Delete' }).click();
await this.page.getByRole('button', { name: 'Yes' }).click();
await expect(this.page.getByText('No Quotation found')).toBeVisible();
}

async voidQuotation() {
await this.page.goto(`${ERPNEXT_URL}/app/quotation`);
await this.searchQuotation();
await this.page.getByRole('checkbox', { name: 'Select All' }).check();
await delay(2500);
await this.page.getByRole('button', { name: 'Actions' }).click();
await this.page.getByRole('link', { name: 'Cancel' }).click();
await this.page.getByRole('button', { name: 'Yes' }).click();
}

async deleteCustomer() {
await this.page.goto(`${ERPNEXT_URL}/app/customer`);
await this.searchCustomer();
await this.page.getByRole('checkbox', { name: 'Select All' }).check();
await delay(2500);
await this.page.getByRole('button', { name: 'Actions' }).click();
await this.page.getByRole('link', { name: 'Delete' }).click();
await this.page.getByRole('button', { name: 'Yes' }).click();
await expect(this.page.getByText('No Customer found')).toBeVisible();
}
}
39 changes: 23 additions & 16 deletions e2e/utils/functions/openmrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ export class OpenMRS {
await this.page.getByRole('button', { name: 'Sign In' }).click();
} else {
await this.page.locator('#username').fill(`${process.env.O3_USERNAME_ON_FOSS}`);
await this.page.waitForTimeout(1000);
await delay(1000);
await this.page.getByRole('button', { name: 'Continue' }).click();
await this.page.locator('#password').fill(`${process.env.O3_PASSWORD_ON_FOSS}`);
await this.page.waitForTimeout(1000);
await delay(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 delay(5000);
await this.expectAllButtonsToBePresent();
await this.waitHomePageToLoad();
}

async createPatient() {
Expand All @@ -54,7 +54,7 @@ export class OpenMRS {
updatedFirstName: `${(Math.random() + 1).toString(36).substring(2)}`
}
patientFullName = patientName.firstName + ' ' + patientName.givenName;
await this.expectAllButtonsToBePresent();
await this.waitHomePageToLoad();
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();
Expand Down Expand Up @@ -137,12 +137,12 @@ export class OpenMRS {
}

async endPatientVisit() {
await this.searchPatient(`${patientFullName}`)
await this.searchPatient(`${patientName.firstName + ' ' + patientName.givenName}`)
await this.page.getByRole('button', { name: 'Actions', exact: true }).click();
await this.page.getByRole('menuitem', { name: 'End visit' }).click();
await this.page.getByRole('button', { name: 'danger End Visit' }).click();
await expect(this.page.getByText('Visit ended')).toBeVisible();
await this.page.getByRole('button', { name: 'Close' }).click();
await this.page.getByRole('button', { name: 'Close', exact: true }).click();
}

async deletePatient() {
Expand All @@ -153,7 +153,6 @@ export class OpenMRS {
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 this.page.getByRole('link', { name: 'Log out' }).click();
}

async addPatientCondition() {
Expand Down Expand Up @@ -201,6 +200,18 @@ export class OpenMRS {
await expect(appointmentStatus).toHaveText('Scheduled');
}

async createLabOrder() {
await this.page.getByLabel('Order basket', { exact: true }).click();
await this.page.getByRole('button', { name: 'Add', exact: true }).nth(1).click();
await this.page.getByPlaceholder('Search for a test type').fill('Urobilinogen');
await delay(3000);
await this.page.getByRole('button', { name: 'Order form' }).click();
await this.page.getByRole('button', { name: 'Save order' }).click();
await this.page.getByRole('button', { name: 'Sign and close' }).click();
await expect(this.page.getByText('Placed orders')).toBeVisible();
await delay(3000);
}

async goToLabOrderForm() {
await this.page.getByLabel('Clinical forms').click();
await delay(3000);
Expand Down Expand Up @@ -245,12 +256,10 @@ export class OpenMRS {

async createDrugOrder() {
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 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();
Expand All @@ -272,12 +281,10 @@ export class OpenMRS {

async createDrugOrderWithFreeTextDosage() {
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 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');
Expand All @@ -296,13 +303,14 @@ export class OpenMRS {
async editDrugOrder() {
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');
await this.page.getByPlaceholder('Frequency').click();
await this.page.getByText('Thrice daily').click();
await this.page.getByLabel('Duration', { exact: true }).clear();
await this.page.getByLabel('Duration', { exact: true }).fill('6');
await this.page.getByLabel('Quantity to dispense').clear();
await this.page.getByLabel('Quantity to dispense').fill('8');
await this.page.getByRole('button', { name: 'Save order' }).focus();
await this.page.getByRole('button', { name: 'Save order' }).dispatchEvent('click');
await expect(this.page.getByText('Sign and close')).toBeVisible();
Expand Down Expand Up @@ -394,10 +402,9 @@ export class OpenMRS {
await this.page.getByRole('link', { name: 'Log out' }).click();
}

async expectAllButtonsToBePresent() {
async waitHomePageToLoad() {
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: 'My Account' })).toBeEnabled();
await expect(this.page.getByRole('button', { name: 'App Menu' })).toBeEnabled();
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
],
"scripts": {
"e2e-tests-pro": "npx playwright test",
"e2e-tests-foss": "npx playwright test odoo-openmrs openmrs-senaite"
"e2e-tests-foss": "npx playwright test odoo-openmrs erpnext-openmrs openmrs-senaite"
},
"publishConfig": {
"registry": "https://nexus.mekomsolutions.net/repository/npm-public/"
Expand Down

0 comments on commit f2a626b

Please sign in to comment.