Skip to content

feat: add E2E Playwright tests for billing settings pages#16230

Open
pruthvirajpatil2024 wants to merge 1 commit intoohcnetwork:developfrom
pruthvirajpatil2024:test/billing-settings-e2e-tests
Open

feat: add E2E Playwright tests for billing settings pages#16230
pruthvirajpatil2024 wants to merge 1 commit intoohcnetwork:developfrom
pruthvirajpatil2024:test/billing-settings-e2e-tests

Conversation

@pruthvirajpatil2024
Copy link
Copy Markdown

@pruthvirajpatil2024 pruthvirajpatil2024 commented Mar 30, 2026

Proposed Changes

Fixes #16208

  • Add E2E Playwright test suite for billing settings pages covering Tax Codes, Tax Components, Informational Codes, Discount Configuration, and Invoice Number Expression
  • Tests verify list rendering, search filtering (matching, non-matching, and clear), read-only view, edit/save flow with success feedback, and cancel-restore behaviour
  • All UI strings sourced from en.json i18n keys — no hardcoded English literals in assertions
  • Search empty-state assertions use { exact: true } to prevent strict-mode violations from DOM-nesting multi-matches
  • Toast assertion for discount configuration save uses a tighter 5 s timeout to account for ephemeral dismissal
  • test.setTimeout(120000) set at file level to give beforeEach (navigation + waitFor) sufficient budget within the global 60 s test timeout
  • searchFilterSuite() helper eliminates duplication across the three identical table-page suites

Tagging: @ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate the bug or test the new feature.
  • Update product documentation.
  • Ensure that UI text is placed in I18n files.
  • Prepare a screenshot or demo video for the changelog entry and attach it to the issue.
  • Request peer reviews.
  • Complete QA on mobile devices.
  • Complete QA on desktop devices.
  • Add or update Playwright tests for related changes

@pruthvirajpatil2024 pruthvirajpatil2024 requested review from a team March 30, 2026 17:51
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@github-actions
Copy link
Copy Markdown

⚠️ Merge Checklist Incomplete

Thank you for your contribution! To help us review your PR efficiently, please complete the merge checklist in your PR description.

Your PR will be reviewed once you have marked the appropriate checklist items.

To update the checklist:

  • Change - [ ] to - [x] for completed items
  • Only check items that are relevant to your PR
  • Leave items unchecked if they don't apply

The checklist helps ensure code quality, testing coverage, and documentation are properly addressed.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Walkthrough

A new test file is added for facility billing settings providing end-to-end coverage for search/filter functionality across three table-based pages and edit/save flows for two configuration pages. Tests use authenticated context, role-based locators, and localized strings from en.json.

Changes

Cohort / File(s) Summary
Billing Settings E2E Tests
tests/facility/settings/billing/billingSetting.spec.ts
Introduces Playwright E2E tests covering search/filter flows for Tax Codes, Tax Components, and Informational Codes pages, plus edit/save flows for Discount Configuration and Invoice Number Expression. Includes reusable searchFilterSuite() helper, authenticated test context via storage state, and beforeAll fixture for shared facilityId derivation. All assertions use role-based locators and i18n-compliant strings from en.json.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding E2E Playwright tests for billing settings pages.
Linked Issues check ✅ Passed The PR covers all five pages (Tax Codes, Tax Components, Informational Codes, Discount Configuration, Billing Settings) and implements all required test flows: list rendering, search/filter (matching, non-matching, clear), read-only views, and edit/save/cancel behaviors [#16208]. Uses i18n keys and role-based locators as specified.
Out of Scope Changes check ✅ Passed All changes are directly scoped to adding E2E tests for billing settings as specified in issue #16208; no unrelated modifications to production code or other systems detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description comprehensively covers all proposed changes, includes the issue reference (#16208), details test coverage, implementation approach, and completes the required merge checklist with appropriate items marked.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/facility/settings/billing/billingSetting.spec.ts`:
- Around line 191-210: Capture the current invoice expression text from the
read-only view before entering edit mode, then assert the textbox (obtained via
page.getByRole("textbox", { name: en.invoice_number_expression }) assigned to
input) is prefilled with that exact value after clicking the Edit button; after
clicking Cancel assert the read-only heading/field again shows the original
captured expression. Apply the same change to the similar test that checks
cancel behavior (the second test for invoice_number_expression) so both verify
preservation of the stored expression rather than only presence of
headings/buttons.
- Around line 152-166: Capture the original billing value before editing in the
test "modifying values and saving shows success toast" (e.g., read current value
from page.getByLabel(en.max_applicable_discounts) into a variable), perform the
fill/click/asserts as written, and then add a finally block that restores the
original value by refilling the saved original and clicking the save button (use
the same selectors: page.getByLabel(en.max_applicable_discounts),
page.getByRole("button", { name: en.save })). Apply the same pattern to the
other save test (the one around lines 212-232) so both tests always revert
backend state even on failures.
- Around line 126-179: The tests lack a state oracle: in the "shows current
values in read-only view on navigation" and subsequent tests (functions
referencing en.max_applicable_discounts, en.applicability_order, maxInput, and
en.discount_configuration_saved) assert only visibility; update them to read the
current displayed values (capture the read-only text for
max_applicable_discounts and applicability_order), assert those match the
expected seed/state, then when clicking Edit assert the inputs (maxInput and the
applicability_order input) are pre-filled with those exact values; for the save
test, choose a new value deterministically different from the current value
(e.g., read current value and increment or toggle) before filling and saving,
then assert the read-only view now shows the new value and the success toast
(en.discount_configuration_saved); for the cancel test, record the original
read-only values, change inputs, click cancel, and assert the read-only view
still shows the original values and the inputs are hidden.
- Around line 29-35: The beforeEach hooks (test.beforeEach) are each waiting up
to the global 60s Playwright timeout (via .waitFor with timeout: 60000) which
exhausts the test budget; add a longer test timeout for this spec by calling
test.setTimeout(...) near the top of the file (before the beforeEach blocks) or
increase timeouts on the specific hooks/tests; update the file to call
test.setTimeout(120000) (or another suitable value) before the test.beforeEach
usages so the beforeEach waits don't consume the entire test timeout (also apply
the same change for the other beforeEach blocks referenced).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0bd45c5d-b35e-439a-b87c-db1e3d65adbe

📥 Commits

Reviewing files that changed from the base of the PR and between f05efdb and 394c025.

📒 Files selected for processing (1)
  • tests/facility/settings/billing/billingSetting.spec.ts

Comment on lines +29 to +35
test.beforeEach(async ({ page }) => {
await page.goto(`/facility/${facilityId}${route}`);
// Wait for the search input to appear — proves auth + facility data loaded
await page
.getByPlaceholder(placeholder)
.waitFor({ state: "visible", timeout: 60000 });
});
Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
set -euo pipefail

fd 'playwright\.config\.(ts|js|mjs|cjs)$' . -x sh -c '
  printf "\n--- %s ---\n" "$1"
  sed -n "1,220p" "$1"
' sh {}

printf "\n--- timeout usage in tests/facility/settings/billing/billingSetting.spec.ts ---\n"
sed -n '1,260p' tests/facility/settings/billing/billingSetting.spec.ts \
  | nl -ba \
  | rg -n 'setTimeout|waitFor\(|timeout:'

Repository: ohcnetwork/care_fe

Length of output: 2995


🏁 Script executed:

#!/bin/bash
# Check for waitFor and setTimeout in the spec file
echo "=== Looking for waitFor calls with timeout values ==="
rg 'waitFor\(' tests/facility/settings/billing/billingSetting.spec.ts -A 2 -B 1

echo ""
echo "=== Looking for test.setTimeout calls ==="
rg 'test\.setTimeout|test\.use' tests/facility/settings/billing/billingSetting.spec.ts

Repository: ohcnetwork/care_fe

Length of output: 535


Increase the test timeout before the beforeEach hooks consume the entire budget.

The global Playwright timeout is 60 seconds. Each beforeEach hook waits up to 60 seconds, exhausting the entire timeout before any assertions run. This spec fails to override the timeout.

 test.use({ storageState: "tests/.auth/user.json" });
+test.setTimeout(120000);

Also applies to: lines 116-124, 183-189

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

In `@tests/facility/settings/billing/billingSetting.spec.ts` around lines 29 - 35,
The beforeEach hooks (test.beforeEach) are each waiting up to the global 60s
Playwright timeout (via .waitFor with timeout: 60000) which exhausts the test
budget; add a longer test timeout for this spec by calling test.setTimeout(...)
near the top of the file (before the beforeEach blocks) or increase timeouts on
the specific hooks/tests; update the file to call test.setTimeout(120000) (or
another suitable value) before the test.beforeEach usages so the beforeEach
waits don't consume the entire test timeout (also apply the same change for the
other beforeEach blocks referenced).

Comment on lines +126 to +179
test("shows current values in read-only view on navigation", async ({
page,
}) => {
await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
await expect(
page.getByText(en.max_applicable_discounts, { exact: true }),
).toBeVisible();
await expect(
page.getByText(en.applicability_order, { exact: true }),
).toBeVisible();
});

test("clicking Edit shows pre-filled input fields", async ({ page }) => {
await page.getByRole("button", { name: en.edit }).click();

const maxInput = page.getByLabel(en.max_applicable_discounts);
await expect(maxInput).toBeVisible();

// Value must be a whole number pre-filled from current config
await expect(maxInput).toHaveValue(/^\d+$/);

await expect(page.getByLabel(en.applicability_order)).toBeVisible();
await expect(page.getByRole("button", { name: en.save })).toBeVisible();
await expect(page.getByRole("button", { name: en.cancel })).toBeVisible();
});

test("modifying values and saving shows success toast", async ({ page }) => {
await page.getByRole("button", { name: en.edit }).click();

const maxInput = page.getByLabel(en.max_applicable_discounts);
const newValue = String(faker.number.int({ min: 1, max: 10 }));
await maxInput.fill(newValue);

await page.getByRole("button", { name: en.save }).click();

await expect(
page.getByText(en.discount_configuration_saved, { exact: true }),
).toBeVisible({ timeout: 5000 });
// Read-only view restored
await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
});

test("cancelling edit restores original values", async ({ page }) => {
await page.getByRole("button", { name: en.edit }).click();

const maxInput = page.getByLabel(en.max_applicable_discounts);
await maxInput.fill("999");

await page.getByRole("button", { name: en.cancel }).click();

// Edit form must be gone — edit button back, inputs hidden
await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
await expect(maxInput).not.toBeVisible();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Discount Configuration needs a real state oracle.

This suite mostly checks labels and mode switches. It never proves that the read-only view shows the current values, that applicability_order is prefilled correctly, that save actually persisted a different value, or that cancel restored the previous one. A blank read-only section or a no-op save can still pass here, especially because the random 1..10 can equal the current value.

♻️ Suggested pattern
   test("modifying values and saving shows success toast", async ({ page }) => {
     await page.getByRole("button", { name: en.edit }).click();

     const maxInput = page.getByLabel(en.max_applicable_discounts);
-    const newValue = String(faker.number.int({ min: 1, max: 10 }));
+    const originalValue = await maxInput.inputValue();
+    const newValue = originalValue === "1" ? "2" : "1";
     await maxInput.fill(newValue);

     await page.getByRole("button", { name: en.save }).click();

     await expect(
       page.getByText(en.discount_configuration_saved, { exact: true }),
     ).toBeVisible({ timeout: 5000 });
-    // Read-only view restored
-    await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
+    await page.getByRole("button", { name: en.edit }).click();
+    await expect(page.getByLabel(en.max_applicable_discounts)).toHaveValue(newValue);
   });

   test("cancelling edit restores original values", async ({ page }) => {
     await page.getByRole("button", { name: en.edit }).click();

     const maxInput = page.getByLabel(en.max_applicable_discounts);
-    await maxInput.fill("999");
+    const originalValue = await maxInput.inputValue();
+    const draftValue = originalValue === "999" ? "998" : "999";
+    await maxInput.fill(draftValue);

     await page.getByRole("button", { name: en.cancel }).click();

-    // Edit form must be gone — edit button back, inputs hidden
-    await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
-    await expect(maxInput).not.toBeVisible();
+    await page.getByRole("button", { name: en.edit }).click();
+    await expect(page.getByLabel(en.max_applicable_discounts)).toHaveValue(originalValue);
   });

Do the same for applicability_order, not just its visibility.

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

In `@tests/facility/settings/billing/billingSetting.spec.ts` around lines 126 -
179, The tests lack a state oracle: in the "shows current values in read-only
view on navigation" and subsequent tests (functions referencing
en.max_applicable_discounts, en.applicability_order, maxInput, and
en.discount_configuration_saved) assert only visibility; update them to read the
current displayed values (capture the read-only text for
max_applicable_discounts and applicability_order), assert those match the
expected seed/state, then when clicking Edit assert the inputs (maxInput and the
applicability_order input) are pre-filled with those exact values; for the save
test, choose a new value deterministically different from the current value
(e.g., read current value and increment or toggle) before filling and saving,
then assert the read-only view now shows the new value and the success toast
(en.discount_configuration_saved); for the cancel test, record the original
read-only values, change inputs, click cancel, and assert the read-only view
still shows the original values and the inputs are hidden.

Comment on lines +152 to +166
test("modifying values and saving shows success toast", async ({ page }) => {
await page.getByRole("button", { name: en.edit }).click();

const maxInput = page.getByLabel(en.max_applicable_discounts);
const newValue = String(faker.number.int({ min: 1, max: 10 }));
await maxInput.fill(newValue);

await page.getByRole("button", { name: en.save }).click();

await expect(
page.getByText(en.discount_configuration_saved, { exact: true }),
).toBeVisible({ timeout: 5000 });
// Read-only view restored
await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore the settings you persist in the save-path tests.

tests/support/facilityId.ts:1-43 reuses a shared facility, so both save tests leave backend state changed for retries and later specs. If one of these tests fails after saving, the retry starts from already-edited billing settings. Restore the original values in a finally block after the success assertions.

Also applies to: 212-232

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

In `@tests/facility/settings/billing/billingSetting.spec.ts` around lines 152 -
166, Capture the original billing value before editing in the test "modifying
values and saving shows success toast" (e.g., read current value from
page.getByLabel(en.max_applicable_discounts) into a variable), perform the
fill/click/asserts as written, and then add a finally block that restores the
original value by refilling the saved original and clicking the save button (use
the same selectors: page.getByLabel(en.max_applicable_discounts),
page.getByRole("button", { name: en.save })). Apply the same pattern to the
other save test (the one around lines 212-232) so both tests always revert
backend state even on failures.

Comment on lines +191 to +210
test("shows current expression in read-only view on navigation", async ({
page,
}) => {
await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
await expect(
page.getByRole("heading", { name: en.invoice_number_expression }).first(),
).toBeVisible();
});

test("clicking Edit shows pre-filled input field", async ({ page }) => {
await page.getByRole("button", { name: en.edit }).click();

const input = page.getByRole("textbox", {
name: en.invoice_number_expression,
});
await expect(input).toBeVisible();
await expect(input).toBeEnabled();
await expect(page.getByRole("button", { name: en.save })).toBeVisible();
await expect(page.getByRole("button", { name: en.cancel })).toBeVisible();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Assert the existing invoice expression, not just the heading.

The current checks only prove that the section renders and that a draft value disappears. If cancel blanks the stored expression instead of restoring it, this suite still passes. Capture the original expression, assert the textbox is prefilled with it, and after cancel verify that same value is shown again.

🧪 Minimal improvement
   test("clicking Edit shows pre-filled input field", async ({ page }) => {
     await page.getByRole("button", { name: en.edit }).click();

     const input = page.getByRole("textbox", {
       name: en.invoice_number_expression,
     });
     await expect(input).toBeVisible();
     await expect(input).toBeEnabled();
+    await expect(input).toHaveValue(/\S+/);
     await expect(page.getByRole("button", { name: en.save })).toBeVisible();
     await expect(page.getByRole("button", { name: en.cancel })).toBeVisible();
   });

   test("cancelling edit restores original expression", async ({ page }) => {
     await page.getByRole("button", { name: en.edit }).click();

     const input = page.getByRole("textbox", {
       name: en.invoice_number_expression,
     });
+    const originalExpression = await input.inputValue();
     await input.fill("TEMP_EXPR_XYZ");

     await page.getByRole("button", { name: en.cancel }).click();

     await expect(page.getByRole("button", { name: en.edit })).toBeVisible();
     await expect(input).not.toBeVisible();
     await expect(page.getByText("TEMP_EXPR_XYZ")).not.toBeVisible();
+    await expect(page.getByText(originalExpression, { exact: true })).toBeVisible();
   });

Also applies to: 234-249

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

In `@tests/facility/settings/billing/billingSetting.spec.ts` around lines 191 -
210, Capture the current invoice expression text from the read-only view before
entering edit mode, then assert the textbox (obtained via
page.getByRole("textbox", { name: en.invoice_number_expression }) assigned to
input) is prefilled with that exact value after clicking the Edit button; after
clicking Cancel assert the read-only heading/field again shows the original
captured expression. Apply the same change to the similar test that checks
cancel behavior (the second test for invoice_number_expression) so both verify
preservation of the stored expression rather than only presence of
headings/buttons.

Copy link
Copy Markdown
Contributor

@Jacobjeevan Jacobjeevan left a comment

Choose a reason for hiding this comment

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

Self-review - do compare with our other tests suites.

(await firstDataRow.getByRole("cell").first().textContent()) ?? "";
const searchTerm = cellText.trim().slice(0, 4);
if (!searchTerm) {
throw new Error(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why? does it make sense to throw an error here?

});
}

searchFilterSuite(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

refer to how other tests are handled; write tests that call a common search function, rather than writing a common fn that wraps tests.

import { expect, test } from "@playwright/test";
import { getFacilityId } from "tests/support/facilityId";

import en from "@/public/locale/en.json";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: Add Playwright E2E tests for Billing Settings (search, filter & edit)

2 participants