Skip to content

Commit ac960da

Browse files
committed
feat: Add Playwright tests and Page Object Models for authentication, login, and home page.
1 parent a75be24 commit ac960da

4 files changed

Lines changed: 113 additions & 7 deletions

File tree

pages/HomePage.ts

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,82 @@
1-
import {Page, Locator, expect} from '@playwright/test';
1+
import { Page, Locator, expect } from '@playwright/test';
22

33
export class HomePage {
44
readonly page: Page;
55
readonly expectedTitle: "Home";
66
readonly loggedInUser: Locator;
77

8-
constructor(page : Page) {
8+
// Header elements
9+
readonly accountLink: Locator;
10+
readonly locationBadge: Locator;
11+
readonly homeIcon: Locator;
12+
readonly welcomeHeading: Locator;
13+
readonly logoutButton: Locator;
14+
15+
// Dashboard Apps
16+
readonly findPatientRecordApp: Locator;
17+
readonly activeVisitsApp: Locator;
18+
readonly captureVitalsApp: Locator;
19+
readonly registerPatientApp: Locator;
20+
readonly appointmentSchedulingApp: Locator;
21+
readonly reportsApp: Locator;
22+
readonly dataManagementApp: Locator;
23+
readonly configureMetadataApp: Locator;
24+
readonly systemAdministrationApp: Locator;
25+
26+
constructor(page: Page) {
927
this.page = page;
1028
this.expectedTitle = "Home";
1129
this.loggedInUser = page.locator('li.nav-item.identifier');
30+
31+
// Initialize Header elements
32+
this.accountLink = page.getByText('admin My Account');
33+
// Note: The location badge might change based on selection, but using the one from codegen for now
34+
// A more robust way would be to look for the ID or class, but sticking to codegen text as requested
35+
this.locationBadge = page.getByRole('link', { name: /Inpatient Ward|Isolation Ward/ }).first();
36+
this.homeIcon = page.getByRole('link').first();
37+
this.welcomeHeading = page.getByRole('heading', { name: 'Logged in as Super User (' });
38+
this.logoutButton = page.getByRole('link', { name: 'Logout ' });
39+
40+
// Initialize Dashboard Apps
41+
this.findPatientRecordApp = page.getByRole('link', { name: ' Find Patient Record' });
42+
this.activeVisitsApp = page.getByRole('link', { name: ' Active Visits' });
43+
this.captureVitalsApp = page.getByRole('link', { name: ' Capture Vitals' });
44+
this.registerPatientApp = page.getByRole('link', { name: ' Register a patient' });
45+
this.appointmentSchedulingApp = page.getByRole('link', { name: ' Appointment Scheduling' });
46+
this.reportsApp = page.getByRole('link', { name: ' Reports' });
47+
this.dataManagementApp = page.getByRole('link', { name: ' Data Management' });
48+
this.configureMetadataApp = page.getByRole('link', { name: ' Configure Metadata' });
49+
this.systemAdministrationApp = page.getByRole('link', { name: ' System Administration' });
1250
}
1351

1452
async verifyHomePage() {
1553
await expect(this.page).toHaveTitle(this.expectedTitle);
1654
await expect(this.loggedInUser).toBeVisible();
1755
}
56+
57+
async verifyDashboardElements() {
58+
// Verify Header
59+
await Promise.all([
60+
expect(this.accountLink).toBeVisible(),
61+
// Location badge might vary, checking if at least one is visible if we want to be strict,
62+
// but for now let's check the one we defined.
63+
// await expect(this.locationBadge).toBeVisible(); // Commenting out as it might be flaky if location changes
64+
expect(this.homeIcon).toBeVisible(),
65+
expect(this.welcomeHeading).toBeVisible(),
66+
expect(this.logoutButton).toBeVisible(),
67+
]);
68+
69+
// Verify Apps
70+
await Promise.all([
71+
expect(this.findPatientRecordApp).toBeVisible(),
72+
expect(this.activeVisitsApp).toBeVisible(),
73+
expect(this.captureVitalsApp).toBeVisible(),
74+
expect(this.registerPatientApp).toBeVisible(),
75+
expect(this.appointmentSchedulingApp).toBeVisible(),
76+
expect(this.reportsApp).toBeVisible(),
77+
expect(this.dataManagementApp).toBeVisible(),
78+
expect(this.configureMetadataApp).toBeVisible(),
79+
expect(this.systemAdministrationApp).toBeVisible(),
80+
]);
81+
}
1882
}

tests/LoginTest.spec.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test } from '@playwright/test';
1+
import { test, expect } from '@playwright/test';
22
import { LoginPage } from '../pages/LoginPage';
33
import { loginData } from '../fixtures/LoginData';
44
import { HomePage } from '../pages/HomePage';
@@ -23,24 +23,34 @@ test.describe('Login Tests', () => {
2323

2424
test.describe("Login Functionality", () => {
2525

26-
test('Successful login', async () => {
26+
test('TC-A.1: Successful Login', async () => {
2727
await loginPage.login(validUser.username, validUser.password, validUser.location);
2828
homePage = new HomePage(loginPage.page);
2929
await homePage.verifyHomePage();
3030
});
3131

32-
test('Unsuccessful login', async () => {
32+
test('TC-A.2: Invalid Credential Login', async () => {
3333
await loginPage.login(invalidUser.username, invalidUser.password, invalidUser.location);
3434
await loginPage.errorMessage.isVisible();
3535
await loginPage.assertErrorMessage(errorMessages.invalidCredentials);
3636
});
37-
test('Login without selecting session location', async () => {
37+
38+
test('TC-A.3: Logout Functionality', async () => {
39+
await loginPage.login(validUser.username, validUser.password, validUser.location);
40+
homePage = new HomePage(loginPage.page);
41+
await homePage.verifyHomePage();
42+
43+
await homePage.logoutButton.click();
44+
await expect(loginPage.page).toHaveTitle("Login");
45+
await expect(loginPage.loginButton).toBeVisible();
46+
});
47+
test('TC-A.4: Login without selecting session location', async () => {
3848
await loginPage.loginWithoutLocation(validUser.username, validUser.password);
3949
await loginPage.errorMessageSessionLocationSelect.isVisible();
4050
await loginPage.assertSessionLocationError(errorMessages.locationRequired);
4151
});
4252

43-
test('Verify session locations', async () => {
53+
test('TC-A.5: Verify session locations', async () => {
4454
await loginPage.verifySessionLocations(sessionLocations);
4555
});
4656

tests/auth.setup.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ setup('authenticate', async ({ page }) => {
1313
fs.mkdirSync(authDir, { recursive: true });
1414
}
1515

16+
// Check if we already have a valid session
17+
if (fs.existsSync(authFile)) {
18+
try {
19+
await page.context().addCookies(JSON.parse(fs.readFileSync(authFile, 'utf-8')).cookies);
20+
await page.goto('https://o2.openmrs.org/openmrs/index.htm'); // Go to home page directly
21+
// If we are redirected to login, then our session is invalid
22+
if (await page.title() === 'Home') {
23+
console.log('Using existing auth state');
24+
return;
25+
}
26+
} catch (e) {
27+
console.log('Error reading auth file or invalid state, re-authenticating...');
28+
}
29+
}
30+
1631
const loginPage = new LoginPage(page);
1732
await loginPage.navigate();
1833
await loginPage.login(loginData.validUser.username, loginData.validUser.password, loginData.validUser.location);

tests/homePagetest.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { test } from '@playwright/test';
2+
import { HomePage } from '../pages/HomePage';
3+
4+
test.describe('Home Page Tests', () => {
5+
6+
let homePage: HomePage;
7+
8+
test.beforeEach(async ({ page }) => {
9+
homePage = new HomePage(page);
10+
await page.goto('/');
11+
});
12+
13+
test('Verify Home Page Elements', async () => {
14+
await homePage.verifyHomePage();
15+
await homePage.verifyDashboardElements();
16+
});
17+
});

0 commit comments

Comments
 (0)