Skip to content

MXWAR-76: add framework-agnostic Playwright e2e foundation for login#100

Open
devvaansh wants to merge 2 commits intoopenMF:devfrom
devvaansh:MXWAR-76-e2e-framework-agnostic-login
Open

MXWAR-76: add framework-agnostic Playwright e2e foundation for login#100
devvaansh wants to merge 2 commits intoopenMF:devfrom
devvaansh:MXWAR-76-e2e-framework-agnostic-login

Conversation

@devvaansh
Copy link
Member

@devvaansh devvaansh commented Mar 17, 2026

Description

This PR introduces a framework-agnostic E2E testing foundation for the React web app to improve regression safety and enable scalable cross-framework test reuse.

The main goal is to keep tests stable as the product grows while minimizing maintenance cost between Angular and React implementations.
To support this, the PR establishes a typed Page Object + selector-contract pattern where test intent remains shared and only selector mappings differ per framework.

This also adds higher-value login coverage beyond basic form checks, including:

remember-me behavior
password visibility toggle behavior
persistence checks for tenant/server local state
These additions were made because login state and tenant context are critical in Mifos workflows and are common regression points in multi-tenant environments.

The PR also introduces CI-friendly behavior so backend-dependent tests can be conditionally skipped via environment flag, allowing the same suite to serve both lightweight UI validation and full-stack runs.

Related issues and discussion

##MXWAR-76

Screenshots, if any

Screenshot 2026-03-18 at 1 15 45 AM
Screen.Recording.2026-03-18.at.1.19.56.AM.mov
Screenshot 2026-03-18 at 1 21 24 AM

Checklist

Please make sure these boxes are checked before submitting your pull request - thanks!

  • If you have multiple commits please combine them into one commit by squashing them.

  • Read and understood the contribution guidelines at CONTRIBUTING.md.

Summary by CodeRabbit

  • Documentation

    • Added E2E testing docs explaining setup, patterns, and run commands.
  • Tests

    • Added comprehensive end-to-end login test suite covering UI, interactions, edge cases, and optional backend flows.
  • New Features

    • Introduced reusable E2E page abstractions and a typed selector contract for consistent test APIs.
  • Chores

    • Added E2E tooling, npm scripts, config, TypeScript settings, and updated ignore patterns for test artifacts.

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

Adds a Playwright-based end-to-end testing foundation: config, TypeScript setup, page-object base and LoginPage, typed selectors, test suite for login flows, .gitignore entries, and README documentation for running and porting tests.

Changes

Cohort / File(s) Summary
Config & Tooling
/.gitignore, package.json, tsconfig.playwright.json, playwright.config.ts
Added Playwright config, CI-aware webServer and reporters, test scripts and devDependencies (@playwright/test, playwright), TypeScript config for Playwright, and new ignore patterns for Playwright artifacts.
Page Object Base
playwright/pages/BasePage.ts
New abstract BasePage implementing navigation, wait/sync helpers, common interactions (click, fill, screenshots, visibility checks) for Playwright pages.
Login Page Object
playwright/pages/login.page.ts
New LoginPage implementing framework-agnostic SELECTORS, locators, login flows, assertions, state checks, and helpers (localStorage access, visibility toggles).
Types
playwright/types/selectors.ts
Added LoginSelectors interface to type selector contracts used by page objects.
Tests
playwright/tests/login.spec.ts
Added comprehensive login test suite exercising UI presence, interactions, validation, toggles, localStorage checks, and optional backend-dependent flows gated by SKIP_BACKEND_TESTS.
Docs
playwright/README.md
New README describing POM-based E2E foundation, SelectorMap pattern, run commands, backend test gating, and porting guidance from other frameworks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  rect rgba(200,230,255,0.5)
    participant Dev as Developer (Test Runner)
    participant PW as Playwright Test
  end
  rect rgba(220,255,200,0.5)
    participant Browser as Headless Browser
    participant AppServer as Dev/Vite Server
    participant Backend as Backend/API
  end

  Dev->>PW: run `npm run test:e2e`
  PW->>AppServer: start webServer (build+preview or dev)
  AppServer-->>PW: server ready
  PW->>Browser: launch browser (chromium)
  Browser->>AppServer: navigate to baseURL (/login)
  PW->>Browser: run LoginPage interactions (fill, click, toggles)
  Browser->>Backend: submit login request (if not skipped)
  Backend-->>Browser: auth response
  Browser-->>PW: page navigation / state changes
  PW->>Dev: test results & reporters (html, list)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • IOhacker

Poem

🐇 I hopped into code with a tiny drum,
Pages and selectors — click, go, and come,
Playwright lights the stage, tests ready to run,
Login leaps, green checks sparkle like sun,
A carrot for CI — now the testing is fun! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a framework-agnostic Playwright e2e foundation for login testing, which is the core objective reflected across all file additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@devvaansh devvaansh force-pushed the MXWAR-76-e2e-framework-agnostic-login branch from 47134fb to 123f072 Compare March 17, 2026 20:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
playwright/pages/BasePage.ts (1)

59-60: Avoid networkidle as the shared base-page default.

This is a flaky default for a reusable POM base: once a page adds polling, analytics, or any long-lived request, every subclass that forgets to override waitForLoad() can start timing out. A lighter default plus a page-specific ready sentinel is usually safer here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/pages/BasePage.ts` around lines 59 - 60, The
BasePage.waitForLoad() uses 'networkidle' which is flaky as a shared default;
change the implementation of BasePage.waitForLoad (method waitForLoad in class
BasePage) to use a lighter wait state such as 'load' or 'domcontentloaded'
(e.g., await this.page.waitForLoadState('load')) and update callers/tests to
override waitForLoad in page-specific subclasses when they require a stronger
sentinel (e.g., polling/analytics pages should implement their own ready
sentinel or call waitForLoadState('networkidle') locally); ensure the method
signature remains async waitForLoad(): Promise<void> and add a brief comment on
BasePage explaining subclasses must override for page-specific readiness.
playwright.config.ts (1)

23-24: Extract the dev server port to a single constant to prevent Playwright waiting on the wrong address.

The baseURL and webServer.url are hardcoded to http://localhost:5173, but the local command is just npm run dev without explicit port enforcement. Vite defaults to 5173, but if that port is already in use, Vite will auto-increment to 5174, 5175, etc. When this happens, Playwright continues waiting on 5173 and the test suite hangs.

The fix extracts a shared port constant and enforces it with --strictPort so Vite fails immediately if the port is unavailable, rather than silently choosing an alternative.

Proposed change
+const port = 5173;
+const baseURL = `http://localhost:${port}`;
+
 export default defineConfig({
   testDir: './playwright/tests',
@@
   use: {
-    baseURL: 'http://localhost:5173',
+    baseURL,
@@
   webServer: {
     command: isCI
-      ? 'npm run build && npx vite preview --port 5173'
-      : 'npm run dev',
-    url: 'http://localhost:5173',
+      ? `npm run build && npx vite preview --port ${port}`
+      : `npm run dev -- --port ${port} --strictPort`,
+    url: baseURL,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright.config.ts` around lines 23 - 24, Extract the dev server port into
a single constant (e.g., DEV_SERVER_PORT = 5173) at the top of
playwright.config.ts and use it to construct baseURL (`baseURL =
\`http://localhost:${DEV_SERVER_PORT}\``) and webServer.url; update the
webServer.command to pass `--port ${DEV_SERVER_PORT} --strictPort` when running
`npm run dev` (or replace the command with `vite --port ${DEV_SERVER_PORT}
--strictPort` if directly invoking Vite) so Vite fails instead of
auto-incrementing ports; ensure the config's use.baseURL and webServer.url
reference this constant (symbols: DEV_SERVER_PORT, use.baseURL, webServer,
webServer.command).
playwright/tests/login.spec.ts (2)

31-34: Strengthen the page-title assertion.

toBeTruthy() is very loose and won’t catch incorrect but non-empty titles. Prefer a stable expected string/pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/tests/login.spec.ts` around lines 31 - 34, The test named 'should
have proper page title' uses page.title() and asserts
expect(title).toBeTruthy(), which is too weak; replace that assertion with a
concrete expectation (e.g., expect(title).toBe('Expected Title') for a known
exact title or expect(title).toMatch(/Partial or regex pattern/) for a stable
pattern) so the test fails on incorrect but non-empty titles; update the
assertion in the test function that calls page.title() accordingly.

102-122: Deduplicate backend-skip guard across backend-only tests.

The same skip check is repeated in each test. Group these tests under one backend-dependent describe block with a single guard.

Refactor sketch
-  test('should successfully login', async () => {
-    test.skip(skipBackendTests, 'Skipping backend-dependent test (SKIP_BACKEND_TESTS set).');
+  test.describe('backend-dependent login flows', () => {
+    test.skip(skipBackendTests, 'Skipping backend-dependent test (SKIP_BACKEND_TESTS set).');
+
+    test('should successfully login', async () => {
+      await loginPage.loginAndWaitForDashboard('mifos', 'password');
+      await loginPage.assertLoginSuccess();
+    });
 
-    await loginPage.loginAndWaitForDashboard('mifos', 'password');
-    await loginPage.assertLoginSuccess();
-  });
+    test('should handle invalid credentials', async () => {
+      await loginPage.login('mifos', 'wrongpassword');
+      await loginPage.assertValidationError();
+      await loginPage.assertOnLoginPage();
+    });
 
-  test('should handle invalid credentials', async () => {
-    test.skip(skipBackendTests, 'Skipping backend-dependent test (SKIP_BACKEND_TESTS set).');
-
-    await loginPage.login('mifos', 'wrongpassword');
-    await loginPage.assertValidationError();
-    await loginPage.assertOnLoginPage();
-  });
-
-  test('codegen baseline: login with mifos credentials', async () => {
-    test.skip(skipBackendTests, 'Skipping backend-dependent test (SKIP_BACKEND_TESTS set).');
-
-    await loginPage.login('mifos', 'password');
-    await loginPage.assertLoginSuccess();
+    test('codegen baseline: login with mifos credentials', async () => {
+      await loginPage.login('mifos', 'password');
+      await loginPage.assertLoginSuccess();
+    });
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/tests/login.spec.ts` around lines 102 - 122, Group the three
backend-dependent tests under a single describe block and apply the skip guard
once at the describe level instead of repeating test.skip in each test: wrap the
tests in describe('backend-dependent: login tests', () => { ... }) and call
test.skip(skipBackendTests, 'Skipping backend-dependent test (SKIP_BACKEND_TESTS
set).') at the start of that describe (or use test.describe.configure({ mode:
'serial' }) with a conditional skip) so the contained tests (those invoking
loginPage.login, loginPage.loginAndWaitForDashboard,
loginPage.assertLoginSuccess, loginPage.assertValidationError,
loginPage.assertOnLoginPage) no longer need individual skip calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@playwright.config.ts`:
- Around line 31-33: Remove the global JSON Accept header configuration from the
Playwright config by deleting the Accept: 'application/json' entry under
extraHTTPHeaders (symbol: extraHTTPHeaders) in playwright.config.ts; tests and
API calls already set appropriate headers via the application's HTTP client
(src/lib/http-client.ts) so remove this redundant global header to restore
realistic browser behavior.

In `@playwright/pages/login.page.ts`:
- Around line 18-24: The selectors in the SELECTORS constant (LoginSelectors)
are brittle because they rely on English text and Tailwind classes; replace
those with stable, locale- and style-independent hooks (e.g., data-testid or
data-qa attributes) used by the Login component, or pin the Playwright locale
explicitly if English strings are required. Update SELECTORS.usernameInput,
.passwordInput, .loginButton, .errorMessage, .loadingIndicator and any other
selectors referenced at lines noted (related to remember/forgot/show-password)
to use data-testid/data-qa attributes (or other semantic attributes the app
exposes) and change tests to query by those attributes (or add the attributes in
the component). Ensure LoginSelectors type matches the new keys and that tests
still target the same logical elements (usernameInput, passwordInput,
loginButton, errorMessage, loadingIndicator).

In `@playwright/tests/login.spec.ts`:
- Around line 12-13: The current skipBackendTests flag uses
!!env.SKIP_BACKEND_TESTS which treats the string "false" as truthy; change the
parsing to interpret SKIP_BACKEND_TESTS explicitly (e.g., const skipBackendTests
= String(env.SKIP_BACKEND_TESTS).toLowerCase() === 'true' or use JSON.parse with
a safe fallback) so only an explicit true value disables backend tests; update
the code references to env and skipBackendTests accordingly.
- Around line 36-42: The test "should support keyboard navigation" currently
checks visibility and can pass falsely; change the final assertion to verify
focus instead of visibility by replacing
expect(loginPage.loginButton).toBeVisible() with an assertion that the locator
is focused (e.g., expect(loginPage.loginButton).toBeFocused()). Ensure the test
still simulates the Tab presses (page.keyboard.press('Tab')) as before and, if
necessary, adjust the number of Tab presses so the loginPage.loginButton locator
receives focus.

---

Nitpick comments:
In `@playwright.config.ts`:
- Around line 23-24: Extract the dev server port into a single constant (e.g.,
DEV_SERVER_PORT = 5173) at the top of playwright.config.ts and use it to
construct baseURL (`baseURL = \`http://localhost:${DEV_SERVER_PORT}\``) and
webServer.url; update the webServer.command to pass `--port ${DEV_SERVER_PORT}
--strictPort` when running `npm run dev` (or replace the command with `vite
--port ${DEV_SERVER_PORT} --strictPort` if directly invoking Vite) so Vite fails
instead of auto-incrementing ports; ensure the config's use.baseURL and
webServer.url reference this constant (symbols: DEV_SERVER_PORT, use.baseURL,
webServer, webServer.command).

In `@playwright/pages/BasePage.ts`:
- Around line 59-60: The BasePage.waitForLoad() uses 'networkidle' which is
flaky as a shared default; change the implementation of BasePage.waitForLoad
(method waitForLoad in class BasePage) to use a lighter wait state such as
'load' or 'domcontentloaded' (e.g., await this.page.waitForLoadState('load'))
and update callers/tests to override waitForLoad in page-specific subclasses
when they require a stronger sentinel (e.g., polling/analytics pages should
implement their own ready sentinel or call waitForLoadState('networkidle')
locally); ensure the method signature remains async waitForLoad(): Promise<void>
and add a brief comment on BasePage explaining subclasses must override for
page-specific readiness.

In `@playwright/tests/login.spec.ts`:
- Around line 31-34: The test named 'should have proper page title' uses
page.title() and asserts expect(title).toBeTruthy(), which is too weak; replace
that assertion with a concrete expectation (e.g., expect(title).toBe('Expected
Title') for a known exact title or expect(title).toMatch(/Partial or regex
pattern/) for a stable pattern) so the test fails on incorrect but non-empty
titles; update the assertion in the test function that calls page.title()
accordingly.
- Around line 102-122: Group the three backend-dependent tests under a single
describe block and apply the skip guard once at the describe level instead of
repeating test.skip in each test: wrap the tests in describe('backend-dependent:
login tests', () => { ... }) and call test.skip(skipBackendTests, 'Skipping
backend-dependent test (SKIP_BACKEND_TESTS set).') at the start of that describe
(or use test.describe.configure({ mode: 'serial' }) with a conditional skip) so
the contained tests (those invoking loginPage.login,
loginPage.loginAndWaitForDashboard, loginPage.assertLoginSuccess,
loginPage.assertValidationError, loginPage.assertOnLoginPage) no longer need
individual skip calls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7329671e-20c4-4d97-97f1-252c6d63e140

📥 Commits

Reviewing files that changed from the base of the PR and between f448968 and 2fb65b1.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • .gitignore
  • package.json
  • playwright.config.ts
  • playwright/README.md
  • playwright/pages/BasePage.ts
  • playwright/pages/login.page.ts
  • playwright/tests/login.spec.ts
  • playwright/types/selectors.ts
  • tsconfig.playwright.json

Comment on lines +31 to +33
extraHTTPHeaders: {
Accept: 'application/json'
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "(playwright\.config|test)" | head -20

Repository: openMF/mifos-x-web-app-react

Length of output: 123


🏁 Script executed:

# Get the full context of playwright.config.ts
wc -l playwright.config.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 95


🏁 Script executed:

# Read the full file
cat -n playwright.config.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 2004


🏁 Script executed:

# Search for any references to Accept header or extraHTTPHeaders in the codebase
rg -i "accept.*header|extraHTTPHeaders" --type ts --type js

Repository: openMF/mifos-x-web-app-react

Length of output: 116


🏁 Script executed:

# Check if there are any API helpers or utilities that handle requests
fd -e ts -e js | xargs grep -l "api\|request\|fetch\|axios" | head -10

Repository: openMF/mifos-x-web-app-react

Length of output: 225


🏁 Script executed:

# Check the login test to see how it uses the configuration
cat -n playwright/tests/login.spec.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 5594


🏁 Script executed:

# Check the HTTP client implementations
cat -n src/lib/http-client.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 1823


🏁 Script executed:

# Check axios configuration
cat -n src/lib/axios.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 1700


🏁 Script executed:

# Search for any API endpoint setup or server-side header expectations
rg -i "accept" --type ts --type js -B 2 -A 2

Repository: openMF/mifos-x-web-app-react

Length of output: 479


Remove the global JSON Accept header from browser traffic.

This sets Accept: 'application/json' for all page navigations and resource requests, which doesn't match real browser behavior. More importantly, the Accept: 'application/json' header is already configured in the HTTP client (src/lib/http-client.ts, line 33) for API requests via axios, making the global setting redundant. The tests will continue working without the global header since API calls already have proper headers set through the application's HTTP client.

Proposed change
-    extraHTTPHeaders: {
-      Accept: 'application/json'
-    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extraHTTPHeaders: {
Accept: 'application/json'
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright.config.ts` around lines 31 - 33, Remove the global JSON Accept
header configuration from the Playwright config by deleting the Accept:
'application/json' entry under extraHTTPHeaders (symbol: extraHTTPHeaders) in
playwright.config.ts; tests and API calls already set appropriate headers via
the application's HTTP client (src/lib/http-client.ts) so remove this redundant
global header to restore realistic browser behavior.

Comment on lines +18 to +24
const SELECTORS: LoginSelectors = {
usernameInput: 'input[name="username"]',
passwordInput: 'input[name="password"]',
loginButton: 'submit|log\\s*in',
errorMessage: '.text-red-500',
loadingIndicator: 'button[type="submit"]:disabled'
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the login page file
fd -t f login.page.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 102


🏁 Script executed:

# Check if file exists and get its content
cat -n playwright/pages/login.page.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 4927


🏁 Script executed:

# Also check the Login.tsx file to verify translation usage
cat -n src/pages/login/Login.tsx | head -250

Repository: openMF/mifos-x-web-app-react

Length of output: 11152


🏁 Script executed:

# Verify the line ranges and get exact content around the mentioned lines
sed -n '41,62p' playwright/pages/login.page.ts

Repository: openMF/mifos-x-web-app-react

Length of output: 666


Use locale- and style-stable selectors for the login flow.

These selectors are tied to English copy (submit|log in, remember me, forgot password, show password|hide password) or a Tailwind utility class (.text-red-500). The Login component renders most of this text through translation keys (i18n), so a locale switch or visual restyle will break the suite without any behavior regression. Prefer stable hooks like data-testid attributes, or pin the Playwright locale if English copy is intentional.

Also applies to lines: 41-43, 51-53, 56-57, 59-62

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/pages/login.page.ts` around lines 18 - 24, The selectors in the
SELECTORS constant (LoginSelectors) are brittle because they rely on English
text and Tailwind classes; replace those with stable, locale- and
style-independent hooks (e.g., data-testid or data-qa attributes) used by the
Login component, or pin the Playwright locale explicitly if English strings are
required. Update SELECTORS.usernameInput, .passwordInput, .loginButton,
.errorMessage, .loadingIndicator and any other selectors referenced at lines
noted (related to remember/forgot/show-password) to use data-testid/data-qa
attributes (or other semantic attributes the app exposes) and change tests to
query by those attributes (or add the attributes in the component). Ensure
LoginSelectors type matches the new keys and that tests still target the same
logical elements (usernameInput, passwordInput, loginButton, errorMessage,
loadingIndicator).

Comment on lines +12 to +13
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};
const skipBackendTests = !!env.SKIP_BACKEND_TESTS;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Parse SKIP_BACKEND_TESTS explicitly to avoid accidental test skips.

!!env.SKIP_BACKEND_TESTS treats "false" as truthy, so backend tests are skipped when the variable is set but intended to be false.

Proposed fix
 const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};
-const skipBackendTests = !!env.SKIP_BACKEND_TESTS;
+const skipBackendTests = ['1', 'true', 'yes'].includes(
+  (env.SKIP_BACKEND_TESTS ?? '').trim().toLowerCase(),
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};
const skipBackendTests = !!env.SKIP_BACKEND_TESTS;
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env ?? {};
const skipBackendTests = ['1', 'true', 'yes'].includes(
(env.SKIP_BACKEND_TESTS ?? '').trim().toLowerCase(),
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/tests/login.spec.ts` around lines 12 - 13, The current
skipBackendTests flag uses !!env.SKIP_BACKEND_TESTS which treats the string
"false" as truthy; change the parsing to interpret SKIP_BACKEND_TESTS explicitly
(e.g., const skipBackendTests = String(env.SKIP_BACKEND_TESTS).toLowerCase() ===
'true' or use JSON.parse with a safe fallback) so only an explicit true value
disables backend tests; update the code references to env and skipBackendTests
accordingly.

Comment on lines +36 to +42
test('should support keyboard navigation', async ({ page }) => {
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');

await expect(loginPage.loginButton).toBeVisible();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keyboard-navigation test currently allows false positives.

This test validates visibility, not focus movement. Since the login button is already visible, broken tab order can still pass.

Suggested assertion upgrade
   test('should support keyboard navigation', async ({ page }) => {
     await page.keyboard.press('Tab');
     await page.keyboard.press('Tab');
     await page.keyboard.press('Tab');
 
-    await expect(loginPage.loginButton).toBeVisible();
+    await expect(loginPage.loginButton).toBeFocused();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should support keyboard navigation', async ({ page }) => {
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await expect(loginPage.loginButton).toBeVisible();
});
test('should support keyboard navigation', async ({ page }) => {
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await expect(loginPage.loginButton).toHaveFocus();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/tests/login.spec.ts` around lines 36 - 42, The test "should
support keyboard navigation" currently checks visibility and can pass falsely;
change the final assertion to verify focus instead of visibility by replacing
expect(loginPage.loginButton).toBeVisible() with an assertion that the locator
is focused (e.g., expect(loginPage.loginButton).toBeFocused()). Ensure the test
still simulates the Tab presses (page.keyboard.press('Tab')) as before and, if
necessary, adjust the number of Tab presses so the loginPage.loginButton locator
receives focus.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
playwright/pages/login.page.ts (1)

51-63: Selectors bypass the typed SELECTORS contract, breaking the porting pattern.

rememberMeCheckbox, forgotPasswordButton, and passwordVisibilityToggle use hardcoded English regex patterns instead of the SELECTORS map. This contradicts the documented principle that "only the SELECTORS block should differ across frameworks" and the LoginSelectors interface doesn't include these fields.

When porting to Angular, developers will need to modify these getters in addition to the SELECTORS block, defeating the framework-agnostic goal.

♻️ Extend LoginSelectors to include all framework-specific selectors

In types/selectors.ts:

 export interface LoginSelectors {
   usernameInput: string
   passwordInput: string
   loginButton: string
   errorMessage: string
   loadingIndicator?: string
+  rememberMeCheckbox: string
+  forgotPasswordButton: string
+  passwordVisibilityToggle: string
 }

Then update SELECTORS in this file:

 const SELECTORS: LoginSelectors = {
   usernameInput: 'input[name="username"]',
   passwordInput: 'input[name="password"]',
   loginButton: 'submit|log\\s*in',
   errorMessage: '.text-red-500',
   loadingIndicator: 'button[type="submit"]:disabled',
+  rememberMeCheckbox: 'remember me',
+  forgotPasswordButton: 'forgot password',
+  passwordVisibilityToggle: 'show password|hide password',
 }

And update the getters to use SELECTORS:

 get rememberMeCheckbox(): Locator {
-  return this.page.getByRole('checkbox', { name: /remember me/i })
+  return this.page.getByRole('checkbox', { name: new RegExp(SELECTORS.rememberMeCheckbox, 'i') })
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@playwright/pages/login.page.ts` around lines 51 - 63, The three getters
rememberMeCheckbox, forgotPasswordButton, and passwordVisibilityToggle bypass
the SELECTORS contract by using hardcoded regexes; extend the LoginSelectors
interface (in types/selectors.ts) to include fields for rememberMe,
forgotPassword, and passwordVisibilityToggle, add corresponding entries to the
SELECTORS map in this file, and update the three getters to reference
SELECTORS.rememberMe / SELECTORS.forgotPassword /
SELECTORS.passwordVisibilityToggle (or the exact names you add) so all
framework-specific selectors live only in the SELECTORS block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@playwright/pages/login.page.ts`:
- Around line 51-63: The three getters rememberMeCheckbox, forgotPasswordButton,
and passwordVisibilityToggle bypass the SELECTORS contract by using hardcoded
regexes; extend the LoginSelectors interface (in types/selectors.ts) to include
fields for rememberMe, forgotPassword, and passwordVisibilityToggle, add
corresponding entries to the SELECTORS map in this file, and update the three
getters to reference SELECTORS.rememberMe / SELECTORS.forgotPassword /
SELECTORS.passwordVisibilityToggle (or the exact names you add) so all
framework-specific selectors live only in the SELECTORS block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7dd0af77-ec29-42f6-b708-c5ef78cc969b

📥 Commits

Reviewing files that changed from the base of the PR and between 2fb65b1 and 16938d2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • .gitignore
  • package.json
  • playwright.config.ts
  • playwright/README.md
  • playwright/pages/BasePage.ts
  • playwright/pages/login.page.ts
  • playwright/tests/login.spec.ts
  • playwright/types/selectors.ts
  • tsconfig.playwright.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • playwright.config.ts
  • .gitignore
  • playwright/pages/BasePage.ts
  • package.json
  • playwright/types/selectors.ts
  • tsconfig.playwright.json
  • playwright/tests/login.spec.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant