Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OZ-541: E2E tests for the flows between O3 and ERPNext. #86

Merged
merged 5 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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