Skip to content

Conversation

@niko-exo
Copy link

@niko-exo niko-exo commented Jan 12, 2026

ref: https://app.clickup.com/t/86b82fa34

86b82fa34 - feat: add url fragment linking to tabs on edit sponsor page

Changelog

  • Add helper functions to handle the relationship between tabs and the url fragment.
  • Add react hook to listen for every fragment change.
  • Update fragment on every tab change.
  • Set default page on the tab general.
  • Set that if an user enters some random fragment, it will be redirected to the general tab.

Links

86b82fa34 - Add TAB deep-linking using URL fragment

Evidence

2026-01-12_09-56-27.mp4

Summary by CodeRabbit

  • New Features

    • Sponsors page tabs use URL hash fragments for direct links/bookmarks; tab selection stays synced with the browser hash.
  • Tests

    • Added comprehensive tests covering tab-to-fragment mapping, hash-driven tab selection, and UI behavior when hashes change.
  • Chores

    • Minor internal exports adjusted to improve testability and module reuse.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

The edit sponsor page was changed to drive tab selection via URL hash fragments: added a tab-to-fragment map and helpers, initialize selected tab from window.location.hash, sync state on external hash changes, and update window.location.hash when tabs change.

Changes

Cohort / File(s) Summary
Tab URL Persistence
src/pages/sponsors/edit-sponsor-page.js
Added tabsToFragmentMap, exported getFragmentFromValue() and getTabFromUrlFragment(). Replaced pathname-based initial-tab logic with hash-driven initialization, added useEffect to resync on window.location.hash changes, updated tab change handler to set window.location.hash, and exported CustomTabPanel with data-testid.
Tests for hash-driven tabs
src/pages/sponsors/__tests__/edit-sponsor-page.test.js
Added tests for getFragmentFromValue and getTabFromUrlFragment, component tests validating hash updates on tab click and tab rendering on hash changes, mocks and Redux state setup, and window.location cleanup.
Reducer exports
src/reducers/sponsors/sponsor-reducer.js, src/reducers/summits/current-summit-reducer.js
Made DEFAULT_STATE a named export in both reducers (no shape or behavior changes).

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant PageComponent
    participant TabState

    Note over Browser,PageComponent: Initial load
    Browser->>PageComponent: load URL (may include `#fragment`)
    PageComponent->>Browser: read window.location.hash
    PageComponent->>PageComponent: getTabFromUrlFragment()
    PageComponent->>TabState: set selectedTab

    Note over Browser,PageComponent: User selects a tab
    Browser->>PageComponent: click tab
    PageComponent->>PageComponent: onTabChange -> getFragmentFromValue()
    PageComponent->>Browser: set window.location.hash
    PageComponent->>TabState: update selectedTab

    Note over Browser,PageComponent: External hash change
    Browser->>PageComponent: window.location.hash changes
    PageComponent->>PageComponent: useEffect observes change
    PageComponent->>TabState: resync selectedTab
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • feat: sponsor cart view #748 — Adds a SponsorCartTab panel (index 5); touches the same edit-sponsor-page tab structure and may conflict with tab indices/ordering.

Suggested reviewers

  • smarcet

Poem

🐇 I hopped through fragments, left a mark so bright,
Tabs now follow hashes, morning, noon, and night.
Click or bookmark, return without a fray,
A rabbit's trail that guides your way. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding URL fragment linking to tabs on the edit sponsor page.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

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: 1

🤖 Fix all issues with AI agents
In @src/pages/sponsors/edit-sponsor-page.js:
- Around line 116-118: The useEffect using window.location.hash in the
dependency array won't re-run on hash changes; replace it with a hashchange
event listener: in the effect that currently calls
setSelectedTab(getTabFromUrlFragment()), register
window.addEventListener('hashchange', handler) where handler calls
setSelectedTab(getTabFromUrlFragment()), call the handler once on mount to
initialize, and remove the listener in the cleanup; remove window.location.hash
from the dependency array (use []).
🧹 Nitpick comments (1)
src/pages/sponsors/edit-sponsor-page.js (1)

46-64: Helper functions look good, consider consolidating the tab definition.

The mapping and helper functions are well-structured. The fallback to index 0 for unrecognized fragments correctly implements the PR requirement.

Consider deriving tabsToFragmentMap from the tabs array (or vice versa) to avoid maintaining two separate lists that must stay in sync. For now, this is acceptable.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9ae9ce and 22f0d4b.

📒 Files selected for processing (1)
  • src/pages/sponsors/edit-sponsor-page.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (2)
src/pages/sponsors/edit-sponsor-page.js (2)

120-123: LGTM!

Setting window.location.hash correctly updates the URL and adds a browser history entry, enabling back/forward navigation between tabs.


155-155: Formatting change acknowledged.

Minor whitespace adjustment in the sx prop—no functional impact.

@fntechgit fntechgit deleted a comment from coderabbitai bot Jan 12, 2026
Copy link

@smarcet smarcet left a comment

Choose a reason for hiding this comment

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

@niko-exo please review

const result = tabsToFragmentMap.indexOf(
window.location.hash.replace("#", "")
);
return result > -1 ? result : 0;
Copy link

Choose a reason for hiding this comment

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

on the initial load
general tab should be selected hence #general should be show by default

 if (result > -1) return result;

  if (window.location.hash) window.location.hash = "general";
  return 0
```;

Copy link
Author

Choose a reason for hiding this comment

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

Yup! If the general fragment is provided, then it will use it. If not it will retrieve 0, which means use the general fragment. Note that the general fragment is included on the 0 position of the array above the function. I think this function is performing what you mean.

@niko-exo niko-exo force-pushed the feature/tab-deep-linking-fragment branch from 22f0d4b to 9c4adf3 Compare January 14, 2026 16:51
@niko-exo
Copy link
Author

#756 Unblocks this pull request.

@niko-exo niko-exo added the BLOCKED Pull request blocked by other ticket label Jan 15, 2026
@niko-exo niko-exo force-pushed the feature/tab-deep-linking-fragment branch from 9c4adf3 to c3e31c6 Compare January 16, 2026 12:12
@niko-exo niko-exo removed the BLOCKED Pull request blocked by other ticket label Jan 16, 2026
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: 3

🤖 Fix all issues with AI agents
In `@src/pages/sponsors/__tests__/edit-sponsor-page.test.js`:
- Around line 144-151: The test's assertion expects window.location.hash without
the leading '#' but JSDOM stores hashes with the '#' prefix; update the
assertion in src/pages/sponsors/__tests__/edit-sponsor-page.test.js to expect
"#forms" instead of "forms" (the component sets the fragment via
getFragmentFromValue() in edit-sponsor-page.js which returns values without '#',
but window.location.hash will include it). Ensure any other assertions in this
test that check window.location.hash are adjusted the same way.
- Line 17: Remove the global.window override that stubs the entire JSDOM window
(the line `global.window = { location: { pathname: "/sponsor-forms/items" } };`)
so tests use the real JSDOM window and its Location API; rely on existing test
setup that uses Object.defineProperty to modify window.location or
window.location.hash when needed, and ensure no other tests expect the stubbed
object.
- Around line 153-217: The test currently only checks existence of tab panels
which is a false positive because CustomTabPanel renders all panels
hidden/visible; update the test to wait for the component to react to the hash
change (use waitFor or findByTestId) and assert visibility instead of mere
existence: after setting location.hash="#general" wait for simple-tabpanel-0 to
be visible (expect(...).toBeVisible()) and that simple-tabpanel-1 is hidden
(expect(...).not.toBeVisible() or expect(...).toHaveAttribute('hidden')); then
after setting hash="#users" wait for simple-tabpanel-1 to be visible and assert
simple-tabpanel-0 is hidden. Use screen.findByTestId or waitFor +
screen.getByTestId and the jest-dom matchers to check visibility; target the
test wrapping EditSponsorPage and the simple-tabpanel-* test ids.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c4adf3 and c3e31c6.

📒 Files selected for processing (4)
  • src/pages/sponsors/__tests__/edit-sponsor-page.test.js
  • src/pages/sponsors/edit-sponsor-page.js
  • src/reducers/sponsors/sponsor-reducer.js
  • src/reducers/summits/current-summit-reducer.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/sponsors/edit-sponsor-page.js
🧰 Additional context used
🧬 Code graph analysis (2)
src/reducers/sponsors/sponsor-reducer.js (15)
src/reducers/summits/current-summit-reducer.js (2)
  • DEFAULT_STATE (217-223)
  • DEFAULT_STATE (217-223)
src/reducers/payment_profiles/payment-profile-reducer.js (1)
  • DEFAULT_STATE (39-42)
src/reducers/sponsors/sponsor-customized-form-reducer.js (1)
  • DEFAULT_STATE (41-43)
src/reducers/sponsors/sponsor-page-forms-list-reducer.js (1)
  • DEFAULT_STATE (29-51)
src/reducers/sponsors/sponsor-list-reducer.js (1)
  • DEFAULT_STATE (25-34)
src/reducers/badges/badge-feature-list-reducer.js (1)
  • DEFAULT_STATE (23-28)
src/reducers/sponsors/sponsor-social-network-reducer.js (1)
  • DEFAULT_STATE (35-38)
src/reducers/sponsors/sponsor-material-reducer.js (1)
  • DEFAULT_STATE (36-39)
src/reducers/badges/badge-type-reducer.js (1)
  • DEFAULT_STATE (42-45)
src/reducers/sponsors/sponsor-advertisement-reducer.js (1)
  • DEFAULT_STATE (39-42)
src/reducers/badges/access-level-list-reducer.js (1)
  • DEFAULT_STATE (23-28)
src/reducers/badges/badge-type-list-reducer.js (1)
  • DEFAULT_STATE (23-28)
src/reducers/badges/access-level-reducer.js (1)
  • DEFAULT_STATE (34-37)
src/reducers/badges/badge-feature-reducer.js (1)
  • DEFAULT_STATE (36-39)
src/reducers/refund_policies/refund-policy-list-reducer.js (1)
  • DEFAULT_STATE (25-30)
src/reducers/summits/current-summit-reducer.js (16)
src/reducers/events/summit-event-reducer.js (1)
  • DEFAULT_STATE (108-115)
src/reducers/tickets/ticket-list-reducer.js (1)
  • DEFAULT_STATE (28-45)
src/reducers/tickets/ticket-reducer.js (1)
  • DEFAULT_STATE (63-67)
src/reducers/attendees/attendee-reducer.js (1)
  • DEFAULT_STATE (56-59)
src/reducers/promocodes/promocode-reducer.js (1)
  • DEFAULT_STATE (78-83)
src/reducers/tickets/ticket-type-reducer.js (1)
  • DEFAULT_STATE (42-45)
src/reducers/events/event-type-reducer.js (1)
  • DEFAULT_STATE (62-65)
src/reducers/locations/location-list-reducer.js (1)
  • DEFAULT_STATE (24-28)
src/reducers/locations/room-reducer.js (1)
  • DEFAULT_STATE (47-50)
src/reducers/events/event-category-list-reducer.js (1)
  • DEFAULT_STATE (25-27)
src/reducers/events/event-category-reducer.js (1)
  • DEFAULT_STATE (51-55)
src/reducers/speakers/speaker-reducer.js (1)
  • DEFAULT_STATE (52-55)
src/reducers/events/event-material-reducer.js (1)
  • DEFAULT_STATE (42-45)
src/reducers/badges/badge-type-reducer.js (1)
  • DEFAULT_STATE (42-45)
src/reducers/locations/floor-reducer.js (1)
  • DEFAULT_STATE (37-40)
src/reducers/locations/location-reducer.js (1)
  • DEFAULT_STATE (67-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (2)
src/reducers/sponsors/sponsor-reducer.js (1)

135-139: LGTM — exporting DEFAULT_STATE is consistent and safe.
This aligns with the repo pattern for reducer default state access.

src/reducers/summits/current-summit-reducer.js (1)

217-223: LGTM — DEFAULT_STATE export is a clean consistency update.
No behavioral impact, just improved reuse.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

DEFAULT_STATE as currentSummitDefaultState
} from "../../../reducers/summits/current-summit-reducer";

global.window = { location: { pathname: "/sponsor-forms/items" } };
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

🧩 Analysis chain

🏁 Script executed:

# Step 1: Verify EditSponsorPage uses hashchange/addEventListener so the real window APIs are required
rg -n "hashchange|addEventListener\(|window\.location\.hash" src/pages/sponsors/edit-sponsor-page.js

Repository: fntechgit/summit-admin

Length of output: 207


🏁 Script executed:

# Step 2: Examine the test file around line 17 to understand context
head -50 src/pages/sponsors/__tests__/edit-sponsor-page.test.js | cat -n

Repository: fntechgit/summit-admin

Length of output: 2017


🏁 Script executed:

# Step 3: Check if there are hash-change tests and understand what the test is doing
rg -n "hashchange|addEventListener|location\.hash" src/pages/sponsors/__tests__/edit-sponsor-page.test.js -A 3 -B 3

Repository: fntechgit/summit-admin

Length of output: 1833


🏁 Script executed:

# Step 4: Examine the actual component implementation to understand hash handling
cat -n src/pages/sponsors/edit-sponsor-page.js | head -150

Repository: fntechgit/summit-admin

Length of output: 5445


🏁 Script executed:

# Step 5: Look for useEffect and hash change logic in the component
rg -n "useEffect|hashchange|addEventListener" src/pages/sponsors/edit-sponsor-page.js -B 2 -A 5

Repository: fntechgit/summit-admin

Length of output: 873


🏁 Script executed:

# Step 6: Check if there's any listener setup or initialization in the component
sed -n '50,200p' src/pages/sponsors/edit-sponsor-page.js | cat -n

Repository: fntechgit/summit-admin

Length of output: 5213


🏁 Script executed:

# Check the specific test that asserts hash value at line 150
sed -n '140,160p' src/pages/sponsors/__tests__/edit-sponsor-page.test.js | cat -n

Repository: fntechgit/summit-admin

Length of output: 732


🏁 Script executed:

# Check if there are any other tests that involve hash manipulation or tab changes
rg -n "should change the tab|fragment change" src/pages/sponsors/__tests__/edit-sponsor-page.test.js -A 20

Repository: fntechgit/summit-admin

Length of output: 652


🏁 Script executed:

# Check what the full test file looks like to understand the testing strategy
wc -l src/pages/sponsors/__tests__/edit-sponsor-page.test.js

Repository: fntechgit/summit-admin

Length of output: 124


🏁 Script executed:

# Check where originalWindowLocation is defined and what it contains
rg -n "originalWindowLocation" src/pages/sponsors/__tests__/edit-sponsor-page.test.js -B 5 -A 2

Repository: fntechgit/summit-admin

Length of output: 1197


🏁 Script executed:

# View the complete test to understand setup/teardown
head -30 src/pages/sponsors/__tests__/edit-sponsor-page.test.js | cat -n

Repository: fntechgit/summit-admin

Length of output: 1348


🏁 Script executed:

# Check if there's a beforeEach or beforeAll that might be resetting window
rg -n "beforeEach|beforeAll|afterEach|afterAll" src/pages/sponsors/__tests__/edit-sponsor-page.test.js -A 5

Repository: fntechgit/summit-admin

Length of output: 245


Remove the global.window override — it prevents JSDOM's Location API from functioning properly.

Line 17 replaces the entire JSDOM window with a minimal stub. This becomes the "reference" used by tests, preventing proper Location semantics when later tests try to mock window.location.hash. Remove the override and keep the real JSDOM window; tests already reset window.location as needed via Object.defineProperty in their setup.

Fix
- global.window = { location: { pathname: "/sponsor-forms/items" } };
📝 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
global.window = { location: { pathname: "/sponsor-forms/items" } };
🤖 Prompt for AI Agents
In `@src/pages/sponsors/__tests__/edit-sponsor-page.test.js` at line 17, Remove
the global.window override that stubs the entire JSDOM window (the line
`global.window = { location: { pathname: "/sponsor-forms/items" } };`) so tests
use the real JSDOM window and its Location API; rely on existing test setup that
uses Object.defineProperty to modify window.location or window.location.hash
when needed, and ensure no other tests expect the stubbed object.

Comment on lines +144 to +151
const usersTabReference = screen.getByText("edit_sponsor.tab.forms");

await act(async () => {
await userEvent.click(usersTabReference);
});

expect(window.location.hash).toBe("forms");
});
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

🧩 Analysis chain

🏁 Script executed:

# First, find and read the test file
cat -n src/pages/sponsors/__tests__/edit-sponsor-page.test.js | head -200

Repository: fntechgit/summit-admin

Length of output: 6883


🏁 Script executed:

# Find and read the EditSponsorPage component
cat -n src/pages/sponsors/edit-sponsor-page.js | head -300

Repository: fntechgit/summit-admin

Length of output: 11190


Fix hash format to match JSDOM behavior.
window.location.hash always includes the leading # when accessed in real JSDOM. The component assigns "forms" (line 129 of edit-sponsor-page.js calls getFragmentFromValue() which returns fragment values without #), but the test assertion should expect the actual stored value with the # prefix.

🔧 Suggested fix
-      expect(window.location.hash).toBe("forms");
+      expect(window.location.hash).toBe("#forms");
📝 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 usersTabReference = screen.getByText("edit_sponsor.tab.forms");
await act(async () => {
await userEvent.click(usersTabReference);
});
expect(window.location.hash).toBe("forms");
});
const usersTabReference = screen.getByText("edit_sponsor.tab.forms");
await act(async () => {
await userEvent.click(usersTabReference);
});
expect(window.location.hash).toBe("#forms");
});
🤖 Prompt for AI Agents
In `@src/pages/sponsors/__tests__/edit-sponsor-page.test.js` around lines 144 -
151, The test's assertion expects window.location.hash without the leading '#'
but JSDOM stores hashes with the '#' prefix; update the assertion in
src/pages/sponsors/__tests__/edit-sponsor-page.test.js to expect "#forms"
instead of "forms" (the component sets the fragment via getFragmentFromValue()
in edit-sponsor-page.js which returns values without '#', but
window.location.hash will include it). Ensure any other assertions in this test
that check window.location.hash are adjusted the same way.

Comment on lines +153 to +217
it("should change the tab rendered on fragment change", async () => {
delete window.location;

Object.defineProperty(window, "location", {
configurable: true,
writable: true,
value: {
...originalWindowLocation,
hash: "#general"
}
});

renderWithRedux(
<EditSponsorPage
history={{}}
location={{
pathname: "/sponsor-forms/items"
}}
match={{}}
/>,
{
initialState: {
currentSummitState: {
currentSummit: defaultSummitEntity,
...currentSummitDefaultState
},
loggedUserState: {
member: {
groups: {}
}
},
currentSummitSponsorshipListState: {
sponsorships: [],
currentPage: 1,
lastPage: 1,
perPage: 100,
order: "order",
orderDir: 1,
totalSponsorships: 0
},
currentSponsorState: {
sponsorships: [],
...currentSponsorDefaultState
}
}
}
);

const generalTabPanel = screen.getByTestId("simple-tabpanel-0");
expect(generalTabPanel).toBeDefined();

delete window.location;

Object.defineProperty(window, "location", {
configurable: true,
writable: true,
value: {
...originalWindowLocation,
hash: "#users"
}
});

const usersTabPanel = screen.getByTestId("simple-tabpanel-1");
expect(usersTabPanel).toBeDefined();
});
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

🧩 Analysis chain

🏁 Script executed:

# First, confirm the test file exists and check the code around lines 153-217
fd "edit-sponsor-page.test.js" --type f

Repository: fntechgit/summit-admin

Length of output: 120


🏁 Script executed:

# Find and explore the test file
cat -n "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" | sed -n '140,220p'

Repository: fntechgit/summit-admin

Length of output: 2648


🏁 Script executed:

# Check the EditSponsorPage component to see how it handles hash changes
fd "edit-sponsor-page" --type f | grep -v test | grep -v ".test"

Repository: fntechgit/summit-admin

Length of output: 105


🏁 Script executed:

# Examine the EditSponsorPage component to understand how it handles hash changes
cat -n "src/pages/sponsors/edit-sponsor-page.js" | head -150

Repository: fntechgit/summit-admin

Length of output: 5445


🏁 Script executed:

# Search for hashchange or hash-related event listeners in the component
rg "hashchange|window.location.hash|hash" "src/pages/sponsors/edit-sponsor-page.js" -A 3 -B 2

Repository: fntechgit/summit-admin

Length of output: 502


🏁 Script executed:

# Check if the component uses useEffect to listen to hash changes
ast-grep --pattern 'useEffect($$$)' "src/pages/sponsors/edit-sponsor-page.js"

Repository: fntechgit/summit-admin

Length of output: 1168


🏁 Script executed:

# Check the test setup and imports to see what testing utilities are available
cat -n "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" | head -50

Repository: fntechgit/summit-admin

Length of output: 2017


🏁 Script executed:

# Search for how hashchange is handled elsewhere in tests
rg "hashchange|HashChangeEvent" --type js -i

Repository: fntechgit/summit-admin

Length of output: 164


🏁 Script executed:

# Check if there's a custom hook or utility for managing hash-based routing
rg "getTabFromUrlFragment|getFragmentFromValue" --type js -A 2 -B 2

Repository: fntechgit/summit-admin

Length of output: 6929


🏁 Script executed:

# Check if there are other tests that use hashchange or similar patterns
rg "hashchange|HashChangeEvent|window.dispatchEvent" --type js -B 2 -A 2

Repository: fntechgit/summit-admin

Length of output: 488


🏁 Script executed:

# Look at the complete test to see if there's proper act() wrapping or waitFor
cat -n "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" | sed -n '100,220p'

Repository: fntechgit/summit-admin

Length of output: 3955


🏁 Script executed:

# Check package.json to see what testing libraries are available
rg "testing-library|jest" package.json

Repository: fntechgit/summit-admin

Length of output: 525


🏁 Script executed:

# Check if the component has any hashchange event listeners
cat -n "src/pages/sponsors/edit-sponsor-page.js" | grep -A 20 "useEffect"

Repository: fntechgit/summit-admin

Length of output: 1893


🏁 Script executed:

# Verify the CustomTabPanel implementation to understand the hidden/visibility logic
cat -n "src/pages/sponsors/edit-sponsor-page.js" | sed -n '69,84p'

Repository: fntechgit/summit-admin

Length of output: 570


🏁 Script executed:

# Check if other components in the codebase listen to hashchange and how they do it
rg "addEventListener.*hash|window.onhashchange" --type js -B 3 -A 5

Repository: fntechgit/summit-admin

Length of output: 788


🏁 Script executed:

# Check how other tests in this file handle async state changes and waits
cat -n "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" | sed -n '70,105p'

Repository: fntechgit/summit-admin

Length of output: 1237


🏁 Script executed:

# Search for patterns of waiting for effects in similar tests
rg "waitFor|act.*hash|fireEvent" "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" -A 3 -B 1

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check if there are other tests that verify visibility of rendered elements
rg "toBeVisible|hidden|role.*tabpanel" --type js -B 2 -A 2 | head -40

Repository: fntechgit/summit-admin

Length of output: 3005


🏁 Script executed:

# Let's look at the complete test and see if there's any waiting mechanism
cat -n "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" | sed -n '150,225p'

Repository: fntechgit/summit-admin

Length of output: 2503


🏁 Script executed:

# Check if renderWithRedux or test setup does anything special with effects
rg "renderWithRedux|beforeEach|afterEach" "src/pages/sponsors/__tests__/edit-sponsor-page.test.js" -A 10 -B 1

Repository: fntechgit/summit-admin

Length of output: 1185


🏁 Script executed:

# Verify how React's useEffect dependency works - check if the component actually responds to hash changes at runtime
cat -n "src/pages/sponsors/edit-sponsor-page.js" | sed -n '119,135p'

Repository: fntechgit/summit-admin

Length of output: 640


🏁 Script executed:

# Check if there are any imports of fireEvent or other event simulation tools in the test
head -30 "src/pages/sponsors/__tests__/edit-sponsor-page.test.js"

Repository: fntechgit/summit-admin

Length of output: 1138


🏁 Script executed:

# Verify if jest-dom provides toBeVisible matcher
rg "jest-dom|toBeVisible|toHaveAttribute" "src/pages/sponsors/__tests__/edit-sponsor-page.test.js"

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check if the component implementation actually listens to hashchange events
rg "addEventListener.*hashchange|on.*hashchange|onhashchange" "src/pages/sponsors/edit-sponsor-page.js"

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check if jest-dom is set up in a setup file
fd "setup" --type f | grep -i jest

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Look for jest configuration to see if jest-dom is imported
rg "setupFilesAfterEnv|testEnvironment" package.json -A 3

Repository: fntechgit/summit-admin

Length of output: 170


🏁 Script executed:

# Test a hypothesis: check if the component would even work correctly with the dependency on window.location.hash
# Search for similar useEffect patterns with window.location
rg "useEffect.*window.location|dependency.*window" --type js -B 2 -A 3

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Let's check the test setup file to understand the environment better
cat "testSetupFile.js" | head -50

Repository: fntechgit/summit-admin

Length of output: 182


🏁 Script executed:

# Verify if the CustomTabPanel's hidden attribute actually hides elements testable with toBeVisible
rg "hidden.*attribute|aria-hidden|display.*none" "src/pages/sponsors/edit-sponsor-page.js" -B 2 -A 2

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Let's trace through what happens - check if there's any event listener setup elsewhere
rg "addEventListener" "src/pages/sponsors/edit-sponsor-page.js"

Repository: fntechgit/summit-admin

Length of output: 48


🏁 Script executed:

# Check the complete rendering of the component to see all tabs
cat -n "src/pages/sponsors/edit-sponsor-page.js" | sed -n '170,250p'

Repository: fntechgit/summit-admin

Length of output: 3688


🏁 Script executed:

# Verify the exact behavior: does the component update when window.location.hash changes at runtime?
# Let's check if maybe there's a different mechanism (like router props) that drives updates
cat -n "src/pages/sponsors/edit-sponsor-page.js" | sed -n '90,130p'

Repository: fntechgit/summit-admin

Length of output: 1384


Assert tab visibility, not just existence.

The test changes window.location.hash but doesn't wait for the component to respond, and only checks toBeDefined() on tab panels. Since CustomTabPanel renders all panels in the DOM (with hidden={value !== index}), checking existence is a false positive—panels exist but may be hidden. Assert that the active panel is visible and the previous one is not.

🔧 Suggested fix
      Object.defineProperty(window, "location", {
        configurable: true,
        writable: true,
        value: {
          ...originalWindowLocation,
          hash: "#users"
        }
      });
+      window.dispatchEvent(new HashChangeEvent("hashchange"));
 
       const usersTabPanel = screen.getByTestId("simple-tabpanel-1");
-      expect(usersTabPanel).toBeDefined();
+      expect(usersTabPanel).toBeVisible();
+      expect(generalTabPanel).not.toBeVisible();
🤖 Prompt for AI Agents
In `@src/pages/sponsors/__tests__/edit-sponsor-page.test.js` around lines 153 -
217, The test currently only checks existence of tab panels which is a false
positive because CustomTabPanel renders all panels hidden/visible; update the
test to wait for the component to react to the hash change (use waitFor or
findByTestId) and assert visibility instead of mere existence: after setting
location.hash="#general" wait for simple-tabpanel-0 to be visible
(expect(...).toBeVisible()) and that simple-tabpanel-1 is hidden
(expect(...).not.toBeVisible() or expect(...).toHaveAttribute('hidden')); then
after setting hash="#users" wait for simple-tabpanel-1 to be visible and assert
simple-tabpanel-0 is hidden. Use screen.findByTestId or waitFor +
screen.getByTestId and the jest-dom matchers to check visibility; target the
test wrapping EditSponsorPage and the simple-tabpanel-* test ids.

@niko-exo niko-exo requested a review from smarcet January 16, 2026 12:24
@niko-exo
Copy link
Author

Ready for review @smarcet

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.

3 participants