diff --git a/e2e/pages/login-page.ts b/e2e/pages/login-page.ts index 2978cc641..5c6bb308f 100644 --- a/e2e/pages/login-page.ts +++ b/e2e/pages/login-page.ts @@ -6,4 +6,14 @@ export class LoginPage { async goto() { await this.page.goto(`${process.env.E2E_BASE_URL}/spa/login`); } + + async login(username: string, password: string) { + await this.goto(); + await this.page.getByLabel(/username/i).fill(username); + await this.page.getByText(/continue/i).click(); + await this.page.getByLabel(/^password$/i).waitFor({ state: 'visible', timeout: 10000 }); + await this.page.getByLabel(/^password$/i).fill(password); + await this.page.getByRole('button', { name: /log in/i }).click(); + await this.page.waitForLoadState('networkidle'); + } } diff --git a/e2e/specs/login.spec.ts b/e2e/specs/login.spec.ts index 661d0816b..9b93fc3b5 100644 --- a/e2e/specs/login.spec.ts +++ b/e2e/specs/login.spec.ts @@ -1,7 +1,20 @@ import { test } from '../core'; -import { expect } from '@playwright/test'; +import { expect, type Page } from '@playwright/test'; import { LoginPage } from '../pages'; +async function selectLocationIfRequired(page: Page) { + const locationPicker = page.getByText(/outpatient clinic/i); + const isLocationPickerVisible = await locationPicker + .waitFor({ state: 'visible', timeout: 2000 }) + .then(() => true) + .catch(() => false); + + if (isLocationPickerVisible) { + await locationPicker.click(); + await page.getByRole('button', { name: /confirm/i }).click(); + } +} + test.use({ storageState: { cookies: [], origins: [] } }); test('Login as Admin user', async ({ page }) => { @@ -19,25 +32,25 @@ test('Login as Admin user', async ({ page }) => { }); await test.step('And I enter my password', async () => { + await page.getByLabel(/^password$/i).waitFor({ state: 'visible', timeout: 10000 }); await page.getByLabel(/^password$/i).fill(`${process.env.E2E_USER_ADMIN_PASSWORD}`); }); await test.step('And I click the `Log in` button', async () => { await page.getByRole('button', { name: /log in/i }).click(); + await page.waitForLoadState('domcontentloaded'); }); - await test.step('And I choose a login location', async () => { - await expect(page).toHaveURL(`${process.env.E2E_BASE_URL}/spa/login/location`); - await page.getByText(/outpatient clinic/i).click(); - await page.getByRole('button', { name: /confirm/i }).click(); + await test.step('And I choose a login location if required', async () => { + await selectLocationIfRequired(page); }); await test.step('Then I should get navigated to the home page', async () => { - await expect(page).toHaveURL(`${process.env.E2E_BASE_URL}/spa/home/service-queues`); + await expect(page).toHaveURL(/\/spa\/home/); }); await test.step('And I should see the location picker in top nav', async () => { - await expect(topNav.getByText(/outpatient clinic/i)).toBeVisible(); + await expect(topNav.getByText(/(outpatient clinic|inpatient ward)/i)).toBeVisible(); }); await test.step('When I click on the my account button', async () => { diff --git a/e2e/specs/navbar.spec.ts b/e2e/specs/navbar.spec.ts index 2b10b898d..0932a5d72 100644 --- a/e2e/specs/navbar.spec.ts +++ b/e2e/specs/navbar.spec.ts @@ -1,7 +1,22 @@ import { test } from '../core'; -import { expect } from '@playwright/test'; +import { expect, type Page } from '@playwright/test'; import { HomePage, LoginPage } from '../pages'; +async function selectLocationIfRequired(page: Page) { + const locationPicker = page.getByText(/outpatient clinic/i); + const isLocationPickerVisible = await locationPicker + .waitFor({ state: 'visible', timeout: 2000 }) + .then(() => true) + .catch(() => false); + + if (isLocationPickerVisible) { + await locationPicker.click(); + await page.getByRole('button', { name: /confirm/i }).click(); + } +} + +test.use({ storageState: { cookies: [], origins: [] } }); + test('View action buttons in the navbar', async ({ page }) => { const loginPage = new LoginPage(page); const homePage = new HomePage(page); @@ -11,10 +26,11 @@ test('View action buttons in the navbar', async ({ page }) => { await loginPage.goto(); await page.getByLabel(/username/i).fill(`${process.env.E2E_USER_ADMIN_USERNAME}`); await page.getByText(/continue/i).click(); + await page.getByLabel(/^password$/i).waitFor({ state: 'visible', timeout: 10000 }); await page.getByLabel(/^password$/i).fill(`${process.env.E2E_USER_ADMIN_PASSWORD}`); await page.getByRole('button', { name: /log in/i }).click(); - await page.getByText(/outpatient clinic/i).click(); - await page.getByRole('button', { name: /confirm/i }).click(); + await page.waitForLoadState('domcontentloaded'); + await selectLocationIfRequired(page); }); await test.step('When I visit the home page', async () => { diff --git a/packages/apps/esm-login-app/src/login/login.component.tsx b/packages/apps/esm-login-app/src/login/login.component.tsx index 4dbb7f826..a314c3b0c 100644 --- a/packages/apps/esm-login-app/src/login/login.component.tsx +++ b/packages/apps/esm-login-app/src/login/login.component.tsx @@ -51,7 +51,10 @@ const Login: React.FC = () => { useEffect(() => { if (showPasswordOnSeparateScreen) { if (showPasswordField) { - passwordInputRef.current?.focus(); + // Only focus password input if it's empty (to preserve browser autofilled values) + if (!passwordInputRef.current?.value) { + passwordInputRef.current?.focus(); + } } else { usernameInputRef.current?.focus(); } @@ -157,6 +160,8 @@ const Login: React.FC = () => { { autoFocus /> {showPasswordOnSeparateScreen ? ( - showPasswordField ? ( - <> + <> + {/* Password input is always in DOM for browser autofill support, but visually hidden until username step is complete */} +
+
+ {showPasswordField ? ( - - ) : ( - - ) + ) : ( + + )} + ) : ( <>