Skip to content

Commit

Permalink
C2C-341: Add configurations for C2C e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kdaud committed Aug 15, 2024
1 parent bacb449 commit 6a5165c
Show file tree
Hide file tree
Showing 11 changed files with 444 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# This file is loaded each time the automated E2E test suite is run.
# It sets a bunch of envvars that drive the behaviour of the automated E2E test suite.
#

# The target environment that will be tested.
# Possible values are dev and uat
TEST_ENVIRONMENT=dev

#
# Bahmni HIS URLs for dev and uat environments.
#

# Bahmni
BAHMNI_URL_DEV=https://c2c-dev.mekomsolutions.net
BAHMNI_URL_UAT=https://c2c-uat.mekomsolutions.net

# Odoo
ODOO_URL_DEV=https://c2c-dev.mekomsolutions.net:8069
ODOO_URL_UAT=https://c2c-uat.mekomsolutions.net:8069

# OpenELIS
OPENELIS_URL_DEV=https://c2c-dev.mekomsolutions.net/openelis
OPENELIS_URL_UAT=https://c2c-uat.mekomsolutions.net/openelis

#
# Bahmni HIS credentials
#

# Bahmni test user credentials
BAHMNI_USERNAME=superman
BAHMNI_PASSWORD=Admin123

# Odoo test user credentials
ODOO_USERNAME=admin
ODOO_PASSWORD=admin

# OpenELIS test user credentials
OpenELIS_USERNAME=admin
OpenELIS_PASSWORD=adminADMIN!
60 changes: 60 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: C2C E2E Tests

on:
schedule:
- cron: "0 0 * * *"
push:
branches: [main]
pull_request:
branches: [main]

workflow_dispatch:
inputs:
environment:
description: 'Choose test environment'
required: true
default: 'dev'
type: choice
options:
- dev
- uat

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:
E2E_TEST_ENVIRONMENT: '${{ github.event.inputs.environment }}'
run: npm run e2e-tests-c2c

- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
node_modules/
test-results/
playwright-report/
playwright/.cache/
.mvn/timing.properties
**/.DS_Store
.idea/
Expand Down
4 changes: 4 additions & 0 deletions e2e/storageState.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"cookies": [],
"origins": []
}
48 changes: 48 additions & 0 deletions e2e/tests/bahmni-odoo-flows.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { test, expect } from '@playwright/test';
import { Odoo } from '../utils/functions/odoo';
import { Bahmni, patientName } from '../utils/functions/bahmni';

let bahmni: Bahmni;
let odoo: Odoo;

test.beforeEach(async ({ page }) => {
bahmni = new Bahmni(page);
odoo = new Odoo(page);

await bahmni.login();
await expect(page.getByText('Registration')).toBeVisible();
await expect(page.getByText('Clinical')).toBeVisible();
await expect(page.getByText('Admin')).toBeVisible();
await expect(page.getByText('Appointment Scheduling')).toBeVisible();
await expect(page.getByText('Patient Documents')).toBeVisible();
});

test('Ordering a lab test for a Bahmni patient creates the corresponding Odoo customer with a filled quotation.', async ({ page }) => {
// setup
await bahmni.registerPatient();

// replay
await bahmni.goToLabSamples();
await page.getByText('Blood', { exact: true }).click();
await page.getByText('Malaria').click();
await bahmni.saveLabOrder();

// verify
await odoo.open();
await expect(page).toHaveURL(/.*web/);
await odoo.searchCustomer();
const customerSelector = await page.locator("tr.o_data_row:nth-child(1) td:nth-child(4)");
await expect(customerSelector).toHaveText(`${patientName.givenName + ' ' + patientName.familyName}`);

const statusSelector = await page.locator("tr.o_data_row:nth-child(1) td:nth-child(8) span");
await expect(statusSelector).toHaveText("Devis");

await page.getByRole('cell', { name: `${patientName.givenName + ' ' + patientName.familyName}` }).click();
const labTest = await page.locator("tr:nth-child(1) td.o_data_cell.o_field_cell.o_list_text.o_section_and_note_text_cell.o_required_modifier");
await expect(labTest).toContainText('Malaria');
});

test.afterEach(async ({ page }) => {
await bahmni.voidPatient();
await page.close();
});
56 changes: 56 additions & 0 deletions e2e/utils/configs/globalSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as dotenv from 'dotenv';
import {
APIRequestContext,
Page,
PlaywrightWorkerArgs,
WorkerFixture,
request,
test as base
}
from '@playwright/test';

dotenv.config();

export const BAHMNI_URL = `${process.env.TEST_ENVIRONMENT}` == 'uat' ? `${process.env.BAHMNI_URL_UAT}` : `${process.env.BAHMNI_URL_DEV}`;
export const ODOO_URL = `${process.env.TEST_ENVIRONMENT}` == 'uat' ? `${process.env.ODOO_URL_UAT}` : `${process.env.ODOO_URL_DEV}`;

async function globalSetup() {
const requestContext = await request.newContext();
const token = Buffer.from(`${process.env.BAHMNI_USERNAME}:${process.env.BAHMNI_PASSWORD}`).toString(
'base64',
);
await requestContext.post(`${process.env.BAHMNI_URL_DEV}`, {
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
});
await requestContext.storageState({ path: 'tests/storageState.json' });
await requestContext.dispose();
}

export const api: WorkerFixture<APIRequestContext, PlaywrightWorkerArgs> = async ({ playwright }, use) => {
const ctx = await playwright.request.newContext({
baseURL: `${process.env.BAHMNI_URL_DEV}/`,
httpCredentials: {
username: process.env.BAHMNI_USERNAME ?? "",
password: process.env.BAHMNI_PASSWORD ?? "",
},
});

await use(ctx);
};

export interface CustomTestFixtures {
loginAsAdmin: Page;
}

export interface CustomWorkerFixtures {
api: APIRequestContext;
}

export const test = base.extend<CustomTestFixtures, CustomWorkerFixtures>({
api: [api, { scope: 'worker' }],
});

export default globalSetup;
67 changes: 67 additions & 0 deletions e2e/utils/functions/bahmni.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Page, expect } from '@playwright/test';
import { BAHMNI_URL } from '../configs/globalSetup';

export var patientName = {
givenName : '',
familyName : '',
}

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

async login() {
await this.page.goto(`${BAHMNI_URL}`)
await this.page.locator('#locale').selectOption('string:en');
await this.page.locator('#username').fill(`${process.env.BAHMNI_USERNAME}`);
await this.page.locator('#password').fill(`${process.env.BAHMNI_PASSWORD}`);
await this.page.locator('#location').selectOption('object:7');
await this.page.getByRole('button', { name: 'Login' }).click();
}

async registerPatient() {
patientName = {
givenName : `e2e_test_${Math.floor(Math.random() * 10000)}`,
familyName : `${(Math.random() + 1).toString(36).substring(2)}`,
}
await this.page.goto(`${BAHMNI_URL}/bahmni/registration`);
await this.page.locator('a').filter({ hasText: 'Create New' }).click();
await this.page.locator('#givenName').fill(`${patientName.givenName}`);
await this.page.locator('#familyName').fill(`${patientName.familyName}`);
await this.page.locator('#gender').selectOption('F');
await this.page.locator('#ageYears').fill('34');
await this.page.locator('#view-content div>ul>li button').click();
await expect(this.page.getByRole('button', { name: 'Save' })).toBeEnabled();
await this.page.getByRole('button', { name: 'Priority' }).click();
await this.page.getByRole('button', { name: 'Save' }).click();
await expect(this.page.getByText('error')).not.toBeVisible();
}

async searchPatient() {
await this.page.locator('#patientIdentifier').fill(`${patientName.familyName}`);
await this.page.getByText(`${patientName.givenName + ' ' + patientName.familyName}`).click();
}

async voidPatient() {
await this.page.goto(`${BAHMNI_URL}/openmrs/admin/patients/index.htm`);
await this.page.getByPlaceholder(' ').type(`${patientName.familyName}`);
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();
}

async goToLabSamples() {
await this.page.locator('i.fa.fa-home').click();
await this.page.getByRole('link', { name: 'Clinical' }).click();
await this.searchPatient();
await this.page.locator('#view-content :nth-child(1).btn--success').click();
await this.page.getByText('Orders', { exact: true }).click();
await expect(this.page.getByText('Lab Samples')).toBeVisible();
}

async saveLabOrder() {
await this.page.getByRole('button', { name: 'Save' }).click();
await expect(this.page.getByText('Saved')).toBeVisible();
}
}
22 changes: 22 additions & 0 deletions e2e/utils/functions/odoo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Page } from '@playwright/test';
import { patientName } from './bahmni';
import { ODOO_URL } from '../configs/globalSetup';

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

async open() {
await this.page.goto(`${ODOO_URL}`);
await this.page.getByPlaceholder('Email').fill(`${process.env.ODOO_USERNAME}`);
await this.page.getByPlaceholder('Password').click();
await this.page.getByPlaceholder('Password').fill(`${process.env.ODOO_PASSWORD}`);
await this.page.locator('button[type="submit"]').click();
}

async searchCustomer() {
await this.page.locator("//a[contains(@class, 'full')]").click();
await this.page.locator('ul.o_menu_apps a:nth-child(2)').click();
await this.page.locator('input.o_searchview_input').fill(`${patientName.givenName + ' ' + patientName.familyName}`);
await this.page.locator('input.o_searchview_input').press('Enter');
}
}
18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "ozone-e2e-c2c",
"version": "next",
"license": "MPL-2.0",
"description": "These are end-to-end automated tests covering Bahmni distro C2C workflows",
"scripts": {
"e2e-tests-c2c": "npx playwright test"
},
"keywords": [],
"devDependencies": {
"@playwright/test": "^1.45.6",
"@types/node": "^22.2.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5",
"typescript": "^5.5.4"
}
}
35 changes: 35 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { devices, PlaywrightTestConfig } from '@playwright/test';
import * as dotenv from 'dotenv';
dotenv.config();

const config: PlaywrightTestConfig = {
testDir: './e2e/tests',
timeout: 3 * 60 * 1000,
expect: {
timeout: 40 * 1000,
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
workers: process.env.CI ? 1 : 1,
retries: 0,
reporter: process.env.CI ? [['junit', { outputFile: 'results.xml' }], ['html']] : [['html']],
use: {
baseURL: `${process.env.BAHMNI_URL_DEV}`,
storageState: 'e2e/storageState.json',
ignoreHTTPSErrors: true,
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chromium'],
launchOptions: {
slowMo: 500
}
},

},
],
};

export default config;
Loading

0 comments on commit 6a5165c

Please sign in to comment.