Skip to content

Commit

Permalink
E2E setup for Openmrs 3.x (#829)
Browse files Browse the repository at this point in the history
  • Loading branch information
Murithijoshua authored Oct 4, 2024
1 parent ecc1aee commit 81cd1e0
Show file tree
Hide file tree
Showing 21 changed files with 631 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions e2e/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# This is an example environment file for configuring dynamic values.
E2E_BASE_URL=https://qa.kenyahmis.org/openmrs
E2E_USER_ADMIN_USERNAME=admin
E2E_USER_ADMIN_PASSWORD=Admin123
E2E_LOGIN_DEFAULT_LOCATION_UUID=44c3efb0-2583-4c80-a79e-1f756a03c0a1
# The above location UUID is for the "Outpatient Clinic" location in the reference application
1 change: 1 addition & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules/
122 changes: 122 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Kenyaemr E2E Testing Setup

This project contains end-to-end (e2e) tests using Playwright with TypeScript. It's structured to provide a scalable and maintainable testing framework.

## Table of Contents

1. [Prerequisites](#prerequisites)
2. [Installation](#installation)
3. [Project Structure](#project-structure)
4. [Writing Tests](#writing-tests)
5. [Running Tests](#running-tests)
6. [Configuration](#configuration)
7. [Best Practices](#best-practices)
8. [Troubleshooting](#troubleshooting)

## Prerequisites

Before you begin, ensure you have the following installed:
- Node.js (v18 or later)
- npm (usually comes with Node.js)

## Installation

1. Clone this repository:
```
git clone <repository-url>
cd <project-directory>
```

2. Install dependencies:
```
npm install
```

3. Install Playwright browsers:
```
npx playwright install
```
4. Copy env.example by
``` sh
cat env.example > .env
```
## Project Structure

The project is structured as follows:

```
e2e/
├── tests/
│ ├── core/
│ │ └── *.ts
│ ├── fixtures/
│ │ └── *.ts
│ ├── pages/
│ │ └── *.ts
│ └── specs/
│ └── *.ts
├── playwright.config.ts
├── package.json
└── tsconfig.json
```
- `core/`: Contains base test setups and utilities.
- `fixtures/`: Holds reusable test fixtures.
- `pages/`: Contains Page Object Models.
- `specs/`: Houses the actual test specifications.
## Writing Tests
1. Create a new page object in `tests/pages/` if needed.
2. Write your test in a new or existing spec file in `tests/specs/`.
3. Use the `test` function from `@playwright/test` to define your tests.
Example:
```typescript
import { test, expect } from '@playwright/test';
import { HomePage } from '../pages/homePage';
test('Home page title is correct', async ({ page }) => {
const homePage = new HomePage(page);
await homePage.navigate();
expect(await homePage.getTitle()).toBe('Expected Title');
});
```

## Running Tests

To run all tests:

```
npm playwright test
```

To run a specific test file:

```
npx playwright test tests/specs/yourTestFile.ts
```

## Configuration

Playwright is configured in `playwright.config.ts`. You can modify this file to change settings such as browsers, viewport size, and more.

## Best Practices

1. Use Page Object Model for better maintainability.
2. Keep tests independent and isolated.
3. Use descriptive test and function names.
4. Utilize fixtures for reusable setup and teardown.
5. Keep sensitive data out of your tests (use environment variables).

## Troubleshooting

If you encounter issues:

1. Ensure all dependencies are installed correctly.
2. Check that Playwright browsers are installed.
3. Verify that your `playwright.config.ts` is correctly set up.
4. Look for error messages in the console output.

For more help, consult the [Playwright documentation](https://playwright.dev/docs/intro).
6 changes: 6 additions & 0 deletions e2e/env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# This is an example environment file for configuring dynamic values.
E2E_BASE_URL=https://qa.kenyahmis.org/openmrs
E2E_USER_ADMIN_USERNAME=admin
E2E_USER_ADMIN_PASSWORD=Admin123
E2E_LOGIN_DEFAULT_LOCATION_UUID=44c3efb0-2583-4c80-a79e-1f756a03c0a1
# The above location UUID is for the "Outpatient Clinic" location in the reference application
119 changes: 119 additions & 0 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "e2e",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@playwright/test": "^1.47.2",
"@types/node": "^22.7.4",
"typescript": "^5.6.2"
},
"dependencies": {
"dotenv": "^16.4.5"
}
}
68 changes: 68 additions & 0 deletions e2e/playwright-report/index.html

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { devices, PlaywrightTestConfig } from '@playwright/test';
import * as dotenv from 'dotenv';
dotenv.config();

// See https://playwright.dev/docs/test-configuration.
const config: PlaywrightTestConfig = {
testDir: 'tests',
timeout: 3 * 60 * 1000,
expect: {
timeout: 40 * 1000,
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: 0,
reporter: process.env.CI ? [['junit', { outputFile: 'results.xml' }], ['html']] : [['html']],
globalSetup: require.resolve('./tests/core/global-setup'),
use: {
baseURL: `${process.env.E2E_BASE_URL}/spa/`,
storageState: 'storageState.json',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
],
};

export default config;
15 changes: 15 additions & 0 deletions e2e/storageState.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"cookies": [
{
"name": "JSESSIONID",
"value": "EB1804BCC4968A7ECDDDC5F33F5CEBF6",
"domain": "qa.kenyahmis.org",
"path": "/openmrs",
"expires": -1,
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
}
],
"origins": []
}
4 changes: 4 additions & 0 deletions e2e/test-results/.last-run.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}
33 changes: 33 additions & 0 deletions e2e/tests/core/global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

import { request } from '@playwright/test';
import * as dotenv from 'dotenv';

dotenv.config();

/**
* This configuration is to reuse the signed-in state in the tests
* by log in only once using the API and then skip the log in step for all the tests.
*
* https://playwright.dev/docs/auth#reuse-signed-in-state
*/

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`, {
data: {
sessionLocation: process.env.E2E_LOGIN_DEFAULT_LOCATION_UUID,
locale: 'en',
},
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${token}`,
},
});
await requestContext.storageState({ path: 'storageState.json' });
await requestContext.dispose();
}

export default globalSetup;
1 change: 1 addition & 0 deletions e2e/tests/core/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './test';
15 changes: 15 additions & 0 deletions e2e/tests/core/location-selector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Page } from '@playwright/test';

export function addURLChangeListener(page: Page) {
page.on('framenavigated', async (frame) => {
if (frame === page.mainFrame()) {
const currentURL = frame.url();

if (currentURL.includes('/login/location')) {
const firstCheckbox = page.locator('input[type="radio"]').first();
await firstCheckbox.check({ force: true });
await page.locator('button:has-text("Confirm")').click();
}
}
});
}
Loading

0 comments on commit 81cd1e0

Please sign in to comment.