Skip to content

Commit 0e4425b

Browse files
committed
WEB-890: Implement storageState authentication bypass for Playwright E2E
Signed-off-by: DeathGun44 <krishnamewara841@gmail.com>
1 parent 7886c79 commit 0e4425b

6 files changed

Lines changed: 104 additions & 17 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,4 @@ src/environments/.env.ts
6969
/playwright-report/
7070
/blob-report/
7171
/playwright/.cache/
72+
/playwright/.auth/

playwright.config.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* Copyright since 2025 Mifos Initiative
33
*
44
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -43,8 +43,8 @@ export default defineConfig({
4343

4444
// Global test settings
4545
use: {
46-
// Base URL for the Angular app
47-
baseURL: 'http://localhost:4200',
46+
// Base URL for the Angular app (configurable via env for CI)
47+
baseURL: process.env.E2E_BASE_URL || 'http://localhost:4200',
4848

4949
// Handle self-signed certificates from Fineract backend
5050
ignoreHTTPSErrors: true,
@@ -73,17 +73,25 @@ export default defineConfig({
7373
// Global test timeout (per test)
7474
timeout: process.env.CI ? 180000 : 120000,
7575

76-
// Configure projects for different browsers
76+
// Configure projects for authentication setup and browser testing
7777
projects: [
78+
{
79+
name: 'setup',
80+
testMatch: /.*\.setup\.ts/,
81+
testDir: './playwright',
82+
retries: process.env.CI ? 2 : 0
83+
},
7884
{
7985
name: 'chromium',
8086
use: {
8187
...devices['Desktop Chrome'],
88+
storageState: 'playwright/.auth/user.json',
8289
// Launch options for handling SSL in headed mode
8390
launchOptions: {
8491
args: ['--ignore-certificate-errors']
8592
}
86-
}
93+
},
94+
dependencies: ['setup']
8795
}
8896
],
8997

playwright/auth.setup.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
import { test as setup, expect } from '@playwright/test';
9+
import fs from 'fs';
10+
import path from 'path';
11+
12+
const authFile = 'playwright/.auth/user.json';
13+
const emptyState: { cookies: never[]; origins: never[] } = { cookies: [], origins: [] };
14+
15+
setup('authenticate', async ({ page, browser }) => {
16+
const authPath = path.resolve(authFile);
17+
const authDir = path.dirname(authPath);
18+
if (!fs.existsSync(authDir)) {
19+
fs.mkdirSync(authDir, { recursive: true });
20+
}
21+
if (fs.existsSync(authPath)) {
22+
fs.unlinkSync(authPath);
23+
}
24+
25+
const username = process.env.E2E_USERNAME || 'mifos';
26+
const password = process.env.E2E_PASSWORD || 'password';
27+
28+
await page.goto('/#/login');
29+
await page.locator('input[formcontrolname="username"]').waitFor({ state: 'visible', timeout: 60000 });
30+
31+
await page.locator('input[formcontrolname="username"]').fill(username);
32+
await page.locator('input[formcontrolname="password"]').fill(password);
33+
await page.getByRole('button', { name: /login/i }).click();
34+
35+
try {
36+
await expect(page).not.toHaveURL(/.*login.*/, { timeout: 30000 });
37+
await expect(page.locator('mat-toolbar')).toBeVisible({ timeout: 10000 });
38+
} catch {
39+
console.log('Auth setup: login failed (no Fineract backend). Writing empty storageState for CI.');
40+
fs.writeFileSync(authPath, JSON.stringify(emptyState));
41+
return;
42+
}
43+
44+
console.log('Auth setup: copying mifosXCredentials from sessionStorage → localStorage');
45+
const credsCopied = await page.evaluate(() => {
46+
const creds = sessionStorage.getItem('mifosXCredentials');
47+
if (!creds) return false;
48+
localStorage.setItem('mifosXCredentials', creds);
49+
return true;
50+
});
51+
52+
if (!credsCopied) {
53+
throw new Error('CRITICAL: mifosXCredentials not found in sessionStorage. ' + 'Did the auth storage key change?');
54+
}
55+
56+
await page.context().storageState({ path: authFile });
57+
console.log('Auth setup: storageState saved to', authFile);
58+
59+
const verifyContext = await browser.newContext({ storageState: authFile });
60+
const verifyPage = await verifyContext.newPage();
61+
await verifyPage.goto('/#/');
62+
await expect(verifyPage).not.toHaveURL(/.*login.*/, { timeout: 30000 });
63+
await verifyContext.close();
64+
console.log('Auth setup: storageState verification passed ✓');
65+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
import { test, expect } from '@playwright/test';
9+
10+
test.describe('Authenticated Smoke Tests', () => {
11+
test('should load dashboard without login redirect', async ({ page }) => {
12+
await page.goto('/#/');
13+
14+
await expect(page).not.toHaveURL(/.*login.*/, { timeout: 30000 });
15+
await expect(page.locator('mat-toolbar')).toBeVisible({ timeout: 10000 });
16+
});
17+
});

playwright/tests/login-responsive.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* Copyright since 2025 Mifos Initiative
33
*
44
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -8,6 +8,8 @@
88
import { test, expect } from '@playwright/test';
99
import { LoginPage } from 'playwright/pages/login.page';
1010

11+
test.use({ storageState: { cookies: [], origins: [] } });
12+
1113
/**
1214
* Login Responsive Layout Tests
1315
*
@@ -209,7 +211,7 @@ test.describe('Login Page - Responsive Layout', () => {
209211

210212
for (const viewport of viewports) {
211213
await page.setViewportSize(viewport);
212-
await page.goto('http://localhost:4200/login');
214+
await page.goto('/#/login');
213215
await page.waitForLoadState('networkidle');
214216

215217
// Check no horizontal scroll

playwright/tests/login.spec.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import { test, expect } from '@playwright/test';
1010
import { LoginPage } from '../pages/login.page';
1111

12+
test.use({ storageState: { cookies: [], origins: [] } });
13+
1214
/**
1315
* Login Smoke Tests
1416
*
@@ -67,8 +69,6 @@ test.describe('Login Page', () => {
6769
await expect(loginPage.loginButton).toBeEnabled();
6870
});
6971

70-
// Skip in CI - requires Fineract backend
71-
test.skip(!!process.env.CI, 'Requires Fineract backend');
7272
test('should successfully login with valid credentials', async () => {
7373
// Perform login with valid credentials
7474
// This uses the login() method which follows the exact codegen sequence
@@ -82,12 +82,8 @@ test.describe('Login Page', () => {
8282
// Attempt login with wrong password
8383
await loginPage.login('mifos', 'wrongpassword');
8484

85-
// Wait for the login attempt to process and any error notification to appear
86-
// The app shows a snackbar notification for authentication errors
87-
await page.waitForTimeout(3000);
88-
89-
// Should remain on login page after failed attempt (URL still contains /login)
90-
await expect(page).toHaveURL(/.*login.*/);
85+
// Should remain on login page after failed attempt
86+
await expect(page).toHaveURL(/.*login.*/, { timeout: 10000 });
9187

9288
// Verify we're still on the login page by checking form elements are visible
9389
await expect(loginPage.usernameInput).toBeVisible();
@@ -98,8 +94,6 @@ test.describe('Login Page', () => {
9894
* Simple test that exactly mirrors the codegen script.
9995
* This is the baseline test generated from codegen.
10096
*/
101-
// Skip in CI - requires Fineract backend
102-
test.skip(!!process.env.CI, 'Requires Fineract backend');
10397
test('codegen baseline: login with mifos credentials', async () => {
10498
// This test uses the exact codegen interaction sequence
10599
await loginPage.login('mifos', 'password');

0 commit comments

Comments
 (0)